效果图:
一次快动作,一次慢动作分解
原理:
贝塞尔曲线画圆的系数求值:
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);
}
}