美文网首页Android自定义View
Android自定义View动画--一个绳子拉动的弹弹球

Android自定义View动画--一个绳子拉动的弹弹球

作者: minminaya | 来源:发表于2017-08-22 16:27 被阅读80次
    运行效果

    灵感来源

    原版

    分析运动规律

    平行时.jpg 最低点.jpg 最高点.jpg

    这是三个状态的截图


    组成

    • 左右俩个圆点
    • 俩个圆角的正方形
    • 一条曲线
    • 一个圆球

    运动规律

    1.颜色渐变:

    状态 左正方形中点颜色值 线中点颜色值 右正方形中点颜色值
    最高 #E42A34 #FF8ED0 #D867DF
    平行 #E72F2C #FD76C1 #C118D9
    最低 #EF2539 #F1358A #BD19D3

    2.运动状态:

    • 俩个正方形中点是静止不变的
    • 正方形和线:正方形来回上下旋转,线是向下弯接着向上弯,可以看到一开始线从水平往下拉的过程,速度是先块后慢的(动画可以选用先加速后减速的插值器AccelerateDecelerateInterpolator
    • 球:由图可以看出,它是1.最低到最高减速,2.最高到水平加速,3.水平到最低减速(例子里简化了该状态,用了匀速上下运动替代了整个过程)

    关键代码

    1.设置画笔颜色渐变和初始化画笔

            //设置渐变,从点A到点B线性渐变
            shader = new LinearGradient(leftRectPointF.x - 10 * factor, leftRectPointF.y, rightRectPointF.x + 10 * factor, rightRectPointF.y, leftGradientColor,
                    rightGradientColor, Shader.TileMode.CLAMP);
            mPaint.setShader(shader);
            //设置拐角圆角
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            mPaint.setStrokeWidth(15);
            //设置画笔为圆笔头
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStyle(Paint.Style.STROKE);
    

    AB俩点指的正方形(旋转后45度后的)左右俩个端点,leftRectPointF和rightRectPointF看下面2中代码端注释

    2. 画圆角正方形并旋转45度(右边正方形为135度)

        /**
         * 正方形在动画里旋转的角度
         */
        private float degree = 0;
         /**
         * 左正方形中点,屏幕中点到左右正方形中心点的距离是45*factor
         */
        private PointF leftRectPointF = new PointF(mScreenWidth / 2 - 45 * factor, mScreenHeight / 2);
        /**
         * 右正方形中点
         */
        private PointF rightRectPointF = new PointF(mScreenWidth / 2 + 45 * factor, mScreenHeight / 2);
        /**
         * 画左右俩旋转45度的正方形
         */
        private void drawLiftAndRightRect(Canvas canvas) {
            //画左边正方形
            canvas.save();
            //这里的旋转要放在最上面,因为canvas的变换是反着来的,这里需要的是先画出正方形再旋转画布
            canvas.rotate(45 - degree, leftRectPointF.x, leftRectPointF.y);
            //正方形的边长为14*factor,一半也就是7*factor
            canvas.drawRoundRect(leftRectPointF.x - 7 * factor, leftRectPointF.y - 7 * factor, leftRectPointF.x + 7 * factor, leftRectPointF.y + 7 * factor, 15, 15, mPaint);
            canvas.restore();
    
            //画右边正方形
            canvas.save();
            //角度值因为是左右相反,和上面相反
            canvas.rotate(45 + degree, rightRectPointF.x, rightRectPointF.y);
            canvas.drawRoundRect(rightRectPointF.x - 7 * factor, rightRectPointF.y - 7 * factor, rightRectPointF.x + 7 * factor, rightRectPointF.y + 7 * factor, 15, 15, mPaint);
            canvas.restore();
        }
    

    3.画贝塞尔曲线

    示例曲线运动规律大致如下

    3.gif

    左端点右端点不变,然后不断变化控制点高度即可。

    本示例的弹弹球的是左右端点上下变换,控制点也上下变化的。

         /**
         * 画曲线
         */
        private void drawLine(Canvas canvas) {
            mPath.reset();
            mPath.moveTo(lineLeftEndPointF.x, lineLeftEndPointF.y);
            mPath.quadTo(quadControllerPointF.x, quadControllerPointF.y, lineRightEndPointF.x, lineRightEndPointF.y);
            canvas.drawPath(mPath, mPaint);
        }
    

    用degree的角度来计算线与正方形接点的坐标

    public void setDegree(float degree) {
            this.degree = degree;
            //算出左边正方形和线连接点坐标
            lineLeftEndPointF = calculatPoint(leftRectPointF, rectDiagonalHalf, degree);
            //右边角度的是180的(余)数
            lineRightEndPointF = calculatPoint(rightRectPointF, rectDiagonalHalf, 180 - degree);
            //控制点
            quadControllerPointF = calculatQuadControllerPointF(45 * factor, degree);
    
            ...
    
            invalidate();
        }
    

    其中calculatPoint函数和calculatQuadControllerPointF如下

    /**
         * 输入起点、长度、旋转角度计算终点
         * <p>
         * 知道一个线段,一个定点,线段旋转角度求终点坐标
         * 根据极坐标系原理 x = pcos(a), y = psin(a)
         *
         * @param startPoint 起点
         * @param length     长度
         * @param angle      旋转角度
         * @return 计算结果点
         */
        private static PointF calculatPoint(PointF startPoint, float length, float angle) {
            float deltaX = (float) Math.cos(Math.toRadians(angle)) * length;
            //符合Android坐标的y轴朝下的标准,和y轴有关的统一减180度
            float deltaY = (float) Math.sin(Math.toRadians(angle - 180)) * length;
            return new PointF(startPoint.x + deltaX, startPoint.y + deltaY);
        }
    

    calculatPoint()是根据极坐标定律,知道起点和线段长度和旋转角度计算端点坐标

    /**
         * 计算控制点
         */
        private PointF calculatQuadControllerPointF(float length, float degree) {
            
            //提高控制点高度
            length += 20 * factor;
            float height = -(float) (Math.tan(Math.toRadians(degree)) * length);
            Log.d("height", "height:" + height);
            return new PointF(mScreenWidth / 2, mScreenHeight / 2 + height);
        }
    

    本来贝塞尔曲线的控制点应该是在曲线中点连线上,但是我试了一下效果,觉得曲线不够弯,所以给它加了20*factor

    4.画球

    /**
         * 画球
         */
        private void drawCircle(Canvas canvas) {
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(circlePointF.x, circlePointF.y, 9 * factor, mPaint);
        }
    

    5.degree数值变化引擎

            //初始化角度引擎
            ObjectAnimator degreeAnimator = ObjectAnimator.ofFloat(this, "degree", 0f, -30f, 0f, 15f, 0, -10, 0, 5, 0);
            degreeAnimator.setDuration(1200);
            degreeAnimator.setRepeatCount(ValueAnimator.INFINITE);
            degreeAnimator.setRepeatMode(ValueAnimator.RESTART);
            degreeAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            degreeAnimator.start();
    

    用了属性动画,设置加速减速插值器,模拟绳子反弹力越来越小的效果

    6.颜色渐变引擎

          //左边渐变边界点颜色值的变化引擎
            ObjectAnimator leftGradientColorAnimator = ObjectAnimator.ofArgb(this, "leftGradientColor", 0xFFE42A34, 0xFFE72F2C, 0xFFEF2539);
            leftGradientColorAnimator.setDuration(1200);
            leftGradientColorAnimator.setRepeatCount(ValueAnimator.INFINITE);
            leftGradientColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
            leftGradientColorAnimator.setInterpolator(new LinearInterpolator());
            leftGradientColorAnimator.start();
            //右边渐变边界点颜色值的变化引擎
            ObjectAnimator rightGradientColorAnimator = ObjectAnimator.ofArgb(this, "rightGradientColor", 0xFFD767DF, 0xFFC118D9, 0xFFBD19D3);
            rightGradientColorAnimator.setDuration(1200);
            rightGradientColorAnimator.setRepeatCount(ValueAnimator.INFINITE);
            rightGradientColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
            rightGradientColorAnimator.setInterpolator(new LinearInterpolator());
            rightGradientColorAnimator.start();
    

    7.球的动画引擎

          //最低往返最高
            final ObjectAnimator circleHeightAnimator2 = ObjectAnimator.ofFloat(this, "circleBottomHeight", 960 + 20 * factor, 960 - 70 * factor);
            circleHeightAnimator2.setDuration(600);
            circleHeightAnimator2.setRepeatCount(ValueAnimator.INFINITE);
            circleHeightAnimator2.setRepeatMode(ValueAnimator.REVERSE);
            circleHeightAnimator2.setInterpolator(new DecelerateInterpolator());
            //延迟160ms,等线先到最低点,再开始球的周期运动
            circleHeightAnimator2.setStartDelay(200);
            circleHeightAnimator2.start();
    

    这里circleBottomHeight定义的是球的底部的y轴坐标值,球中心坐标在setDegree()里实时算出

         public void setDegree(float degree) {
            ...
            
            //球的半径是10*factor
            circlePointF = new PointF(540f, circleBottomHeight - 10 * factor);
            ...
        }
    

    8.其他的看源代码吧....


    不足

    • 球和绳子的互动不是很和谐(因为简化了球的运动步骤)

    源代码:

    CustomViewSet


    end

    相关文章

      网友评论

        本文标题:Android自定义View动画--一个绳子拉动的弹弹球

        本文链接:https://www.haomeiwen.com/subject/sikldxtx.html