Path动画是一种非常特殊的动画,可以理解为动画沿着Path路径移动。构建Path动画有两种方式,第一种是基于TypeEvalutor,该类用于控制View的状态、位置等。另一种是通过PathMeasure,这种可以更加直观的定义路径,同样也可以定义draw绘图等。
方案一:通过TypeEvalutor定义Path动画
曲线动画很常见,但最著名的是贝塞尔曲线动画(贝塞尔曲线的数学原理)
class BezierEvaluator implements TypeEvaluator<PointF>{
@Override
public PointF evaluate(float fraction, PointF startValue,
PointF endValue) {
final float t = fraction;
float oneMinusT = 1.0f - t;
PointF point = new PointF();
PointF point0 = (PointF)startValue;
PointF point1 = new PointF();
point1.set(width, 0);
PointF point2 = new PointF();
point2.set(0, height);
PointF point3 = (PointF)endValue;
point.x = oneMinusT * oneMinusT * oneMinusT * (point0.x)
+ 3 * oneMinusT * oneMinusT * t * (point1.x)
+ 3 * oneMinusT * t * t * (point2.x)
+ t * t * t * (point3.x);
point.y = oneMinusT * oneMinusT * oneMinusT * (point0.y)
+ 3 * oneMinusT * oneMinusT * t * (point1.y)
+ 3 * oneMinusT * t * t * (point2.y)
+ t * t * t * (point3.y);
return point;
}
}
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF)animation.getAnimatedValue();
button.setX(pointF.x);
button.setY(pointF.y);
}
});
方案二:PathMeasure动画
但是还有一类动画就是Path动画,通过PathMeasure实现Object在Path路径上动画
public class DynamicHeartView extends View {
private static final String TAG = "DynamicHeartView";
private static final int PATH_WIDTH = 2;
// 起始点
private static final int[] START_POINT = new int[] {
300, 270
};
// 爱心下端点
private static final int[] BOTTOM_POINT = new int[] {
300, 400
};
// 左侧控制点
private static final int[] LEFT_CONTROL_POINT = new int[] {
450, 200
};
// 右侧控制点
private static final int[] RIGHT_CONTROL_POINT = new int[] {
150, 200
};
private PathMeasure mPathMeasure;
private Paint mPaint;
private Path mPath;
private float[] mCurrentPosition = new float[2];
public DynamicHeartView(Context context) {
super(context);
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(PATH_WIDTH);
mPaint.setColor(Color.RED);
mPath = new Path();
mPath.moveTo(START_POINT[0], START_POINT[1]);
mPath.quadTo(RIGHT_CONTROL_POINT[0], RIGHT_CONTROL_POINT[1], BOTTOM_POINT[0],
BOTTOM_POINT[1]);
mPath.quadTo(LEFT_CONTROL_POINT[0], LEFT_CONTROL_POINT[1], START_POINT[0], START_POINT[1]);
mPathMeasure = new PathMeasure(mPath, true);
mCurrentPosition = new float[2];
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.drawPath(mPath, mPaint);
canvas.drawCircle(RIGHT_CONTROL_POINT[0], RIGHT_CONTROL_POINT[1], 5, mPaint);
canvas.drawCircle(LEFT_CONTROL_POINT[0], LEFT_CONTROL_POINT[1], 5, mPaint);
// 绘制对应目标
canvas.drawCircle(mCurrentPosition[0], mCurrentPosition[1], 10, mPaint);
}
// 开启路径动画
public void startPathAnim(long duration) {
// 0 - getLength()
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
Log.i(TAG, "measure length = " + mPathMeasure.getLength());
valueAnimator.setDuration(duration);
// 减速插值器
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
// 获取当前点坐标封装到mCurrentPosition
mPathMeasure.getPosTan(value, mCurrentPosition, null);
postInvalidate();
}
});
valueAnimator.start();
}
}
http://blog.csdn.net/vrix/article/details/39206975
http://blog.csdn.net/tianjian4592/article/details/47067161
http://www.2cto.com/kf/201503/380377.html
http://blog.csdn.net/androidzhaoxiaogang/article/details/8680330
http://blog.csdn.net/linmiansheng/article/details/18763987