Android自定义View——弹性的圆

效果图:
一次快动作,一次慢动作分解
在这里插入图片描述
原理:
贝塞尔曲线画圆的系数求值:
Approximate a circle with cubic Bézier curves

1、动作分解

不做位移运动,单点动作分解

1.1、从A运动到B:

P2、P3、P4 横坐标的变动
P2/P4纵坐标的变动

P8、P9、P10横坐标的变动
P8/P10纵坐标的变动

1.2、在B处做弹性运动:

先凹进去一部分,再弹出来

P8、P9、P10横坐标的变动
在这里插入图片描述

2、圆的平移动画

做平移动画,绝对坐标

源代码

public class ElasticityCircleView extends View {

    //
    private int CIRCLE_COUNT = 10;
    //
    private int ELASTICITY_COUNT = 5;
    //速度
    private float speed;
    //动作分解的每一个点
    private long animationStartCount;
    //画圆的系数
    private float C = 0.55f;

    private Paint paint;
    private Path path;

    private float mRadius = 60;
    private float rc;

    private PointF point0;
    private PointF point1;
    private PointF point2;
    private PointF point3;
    private PointF point4;
    private PointF point5;
    private PointF point6;
    private PointF point7;
    private PointF point8;
    private PointF point9;
    private PointF point10;
    private PointF point11;

    private PointF center1;
    private PointF center2;
    private PointF moveCircle;

    public ElasticityCircleView(Context context) {
        this(context, null);
    }

    public ElasticityCircleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ElasticityCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(8);
        paint.setAntiAlias(true);
        path = new Path();

        //随意指定圆一和圆二的位置
        center1 = new PointF(100, 400);
        center2 = new PointF(500, 400);
        //速度
        speed = Math.abs(center1.x - center2.x) / CIRCLE_COUNT;
        //移动的圆
        moveCircle = new PointF(100, 400);

        //指定path画圆的12个点
        rc = mRadius * C;
        point0 = new PointF(moveCircle.x + 0, moveCircle.y + -mRadius);
        point1 = new PointF(moveCircle.x + rc, moveCircle.y + -mRadius);
        point2 = new PointF(moveCircle.x + mRadius, moveCircle.y + -rc);
        point3 = new PointF(moveCircle.x + mRadius, moveCircle.y + 0);
        point4 = new PointF(moveCircle.x + mRadius, moveCircle.y + rc);
        point5 = new PointF(moveCircle.x + rc, moveCircle.y + mRadius);
        point6 = new PointF(moveCircle.x + 0, moveCircle.y + mRadius);
        point7 = new PointF(moveCircle.x + -rc, moveCircle.y + mRadius);
        point8 = new PointF(moveCircle.x + -mRadius, moveCircle.y + rc);
        point9 = new PointF(moveCircle.x + -mRadius, moveCircle.y + 0);
        point10 = new PointF(moveCircle.x + -mRadius, moveCircle.y + -rc);
        point11 = new PointF(moveCircle.x + -rc, moveCircle.y + -mRadius);
    }

    //执行动画 快动作40ms,慢动作分解500ms
    public void doAnimation(final int time) {
        animationStartCount = 0;
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (animationStartCount <= CIRCLE_COUNT + ELASTICITY_COUNT) {
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    postInvalidate();
                    calculateData();
                    animationStartCount++;
                }
            }
        }).start();
    }

    //每次重新计算12个点的坐标,采用绝对路径
    private void calculateData() {
        if (animationStartCount > CIRCLE_COUNT) {
            //最后ELASTICITY_COUNT个动作
            float circleLeft = moveCircle.x - mRadius;
            float speedOffset = speed * CIRCLE_COUNT;
            float offsetX1 = (float) (1f / 2 * mRadius * Math.sin(Math.PI / ELASTICITY_COUNT * (animationStartCount - CIRCLE_COUNT)));
            point8.x = circleLeft + speedOffset + offsetX1;
            point9.x = circleLeft + speedOffset + offsetX1;
            point10.x = circleLeft + speedOffset + offsetX1;
        } else {
            //前CIRCLE_COUNT个动作
            float circleLeft = moveCircle.x - mRadius;
            float cicleRight = moveCircle.x + mRadius;
            float offsetX = (float) (0.8 * mRadius * Math.sin(Math.PI / CIRCLE_COUNT * animationStartCount));
            float offsetY = (float) (1f / 4 * mRadius * Math.sin(Math.PI / CIRCLE_COUNT * animationStartCount));
            float speedOffset = speed * animationStartCount;
            point0.x = moveCircle.x + speedOffset;
            point1.x = moveCircle.x + rc + speedOffset;
            point2.x = cicleRight + speedOffset + offsetX;
            point3.x = cicleRight + speedOffset + offsetX;
            point4.x = cicleRight + speedOffset + offsetX;
            point2.y = moveCircle.y - rc - offsetY;
            point4.y = moveCircle.y + rc + offsetY;
            point5.x = moveCircle.x + rc + speedOffset;
            point6.x = moveCircle.x + speedOffset;
            point7.x = moveCircle.x - rc + speedOffset;
            point8.x = circleLeft + speedOffset - offsetX;
            point9.x = circleLeft + speedOffset - offsetX;
            point10.x = circleLeft + speedOffset - offsetX;
            point8.y = moveCircle.y + rc + offsetY;
            point10.y = moveCircle.y + -rc - offsetY;
            point11.x = moveCircle.x - rc + speedOffset;
//        Log.w("zhen0", " point2: " + point2 + " point3: " + point3 + " point4: " + point4);
//        Log.e("zhen0", " point8: " + point8 + " point9: " + point9 + " point10: " + point10);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画背景圆
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(center1.x, center1.y, mRadius + 8, paint);
        canvas.drawCircle(center2.x, center2.y, mRadius + 8, paint);

        //动画圆
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        path.reset();
        //顺时针方向画圆
        path.moveTo(point0.x, point0.y);
        //第一象限
        path.cubicTo(point1.x, point1.y, point2.x, point2.y, point3.x, point3.y);
        //第四象限
        path.cubicTo(point4.x, point4.y, point5.x, point5.y, point6.x, point6.y);
        //第三象限
        path.cubicTo(point7.x, point7.y, point8.x, point8.y, point9.x, point9.y);
        //第二象限
        path.cubicTo(point10.x, point10.y, point11.x, point11.y, point0.x, point0.y);
        canvas.drawPath(path, paint);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值