Path動畫是一種很是特殊的動畫,能夠理解爲動畫沿着Path路徑移動。構建Path動畫有兩種方式,第一種是基於TypeEvalutor,該類用於控制View的狀態、位置等。另外一種是經過PathMeasure,這種能夠更加直觀的定義路徑,一樣也能夠定義draw繪圖等。html
方案一:經過TypeEvalutor定義Path動畫android
曲線動畫很常見,但最著名的是貝塞爾曲線動畫(貝塞爾曲線的數學原理)canvas
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動畫ide
可是還有一類動畫就是Path動畫,經過PathMeasure實現Object在Path路徑上動畫post
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/47067161lua
http://www.2cto.com/kf/201503/380377.html.net
http://blog.csdn.net/androidzhaoxiaogang/article/details/8680330code
http://blog.csdn.net/linmiansheng/article/details/18763987htm