软件编程
位置:首页>> 软件编程>> Android编程>> Android动画之小球拟合动画实例

Android动画之小球拟合动画实例

作者:lqh  发布时间:2023-06-11 00:47:46 

标签:Android,动画

Android动画之小球拟合动画实例

实现效果:

Android动画之小球拟合动画实例

动画组成:

1.通过三阶贝塞尔曲线来拟合圆,拟合系数的由来,以及怎么选控制点.

2.利用画布canvas.translate,以及scale,rotate的方法,来渐变绘制的过程.

3.熟悉拟合过程.

4.不熟悉的话,先绘制辅助点的移动路线,对理解两个圆的分裂的拟合过程有好处.


package com.example.administrator.animationworkdemo.views;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;

import java.util.concurrent.CyclicBarrier;

/**
* 这个例子中,大家可以发现作者的拟合做的并不是很好,连接的地方比较生硬,大家可以思考下如何改善
* 贝塞尔曲线绘制比较复杂,大家在学习过程中,可以仿照示例中的,将辅助点和线绘制出来,这样会看的更清楚一点
*/
public class BallShapeChangeView extends View {

// 使用贝塞尔曲线来拟合圆的magic number
 //C 是三阶贝塞尔曲线拟合 圆的 误差最小  获得控制点的参数.
 private static final float C = 0.551915024494f;
 private Paint mPaint;
 private int mRadiusBig = 120, mRadiusSmall = (int) (mRadiusBig / 2f), mWidth, mHeight, mMimWidth = (int) (mRadiusSmall * 2 * 3)/*fill view mim width*/;
 private float mFraction = 0, mFractionDegree = 0, /*degree*/
     mLength, mDistanceBezier;
 private Path mPathCircle, mPathBezier;
 private ValueAnimator mValueAnimator;
 private float[] mPointData = new float[8];// 4个数据点 顺时针排序,从左边开始
 private float[] mPointCtrl = new float[16];// 8个控制点
 private float[] mPos = new float[2];
 private PathMeasure mPathMeasure;
 private Path mPathBezier2;

public BallShapeChangeView(Context context, AttributeSet attrs) {
   super(context, attrs);
   mPaint = new Paint();
   mPaint.setStyle(Paint.Style.FILL);
   mPaint.setColor(0xFF7C191E);
   mPaint.setAntiAlias(true);
   mPathCircle = new Path();
   mPathBezier = new Path();
   mPathBezier2 = new Path();
   mPathMeasure = new PathMeasure();
   mValueAnimator = ValueAnimator.ofFloat(0, 1, 0);
   mValueAnimator.setDuration(3000);
   mValueAnimator.setRepeatCount(Integer.MAX_VALUE);
   mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
       mFraction = (float) animation.getAnimatedValue();
       mFractionDegree = animation.getAnimatedFraction();
       invalidate();
     }
   });
 }

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   // 为了能够更好的控制绘制的大小和位置,当然,初学者写死也是可以的
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
   mWidth = MeasureSpec.getSize(widthMeasureSpec);
   mHeight = MeasureSpec.getSize(heightMeasureSpec);
   int widthMode = MeasureSpec.getMode(widthMeasureSpec);
   int heightMode = MeasureSpec.getMode(heightMeasureSpec);
   if (widthMode != MeasureSpec.AT_MOST && heightMode != MeasureSpec.AT_MOST) {
     if (mWidth < mMimWidth)
       mWidth = mMimWidth;
     if (mHeight < mMimWidth)
       mHeight = mMimWidth;
   } else if (widthMeasureSpec != MeasureSpec.AT_MOST) {
     if (mWidth < mMimWidth)
       mWidth = mMimWidth;
   } else if (heightMeasureSpec != MeasureSpec.AT_MOST) {
     if (mHeight < mMimWidth)
       mHeight = mMimWidth;
   }
   setMeasuredDimension(mWidth, mHeight);
 }

@Override
 protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   // 通过mFraction来控制绘图的过程,这是常用的一种方式
   canvas.translate(mWidth / 2, mHeight / 2);
   canvas.scale(1, -1);
   canvas.rotate(-360 * mFractionDegree);
   setDoubleCirClePath();
   canvas.drawPath(mPathCircle, mPaint);
   if (mFraction < (1 / 3f)) {// 缩小大圆
     setCirclePath();
     canvas.drawPath(mPathCircle, mPaint);
   } else if (mFraction < 3 / 4f) {// 画贝塞尔曲线
     setBezierPath2();
     canvas.drawPath(mPathBezier, mPaint);
     canvas.drawPath(mPathBezier2, mPaint);
   } else {// 画分离
     //setLastBezierPath();
     //canvas.drawPath(mPathBezier, mPaint);
   }
 }

private void setDoubleCirClePath() {
   mPathCircle.reset();
   if (mFraction < (1 / 3f)) {
     mPathCircle.addCircle(-mRadiusSmall / 2f * mFraction * 3, 0, mRadiusSmall, Path.Direction.CW);
     mPathCircle.addCircle(mRadiusSmall / 2f * mFraction * 3, 0, mRadiusSmall, Path.Direction.CW);
   } else {
     float distance = (mFraction - 1 / 3f) / (2 / 3f) * (mRadiusSmall * 2 + mRadiusSmall / 2f);
     mPathCircle.addCircle(-mRadiusSmall / 2f - distance, 0, mRadiusSmall, Path.Direction.CW);
     mPathCircle.addCircle(mRadiusSmall / 2f + distance, 0, mRadiusSmall, Path.Direction.CW);
   }
 }

// mFraction 0 ~ 1/3
 private void setCirclePath() {
   mPointData[0] = -mRadiusBig + mRadiusSmall / 2f * mFraction * 3f;
   mPointData[1] = 0;
   mPointData[2] = 0;
   mPointData[3] = mRadiusBig - mRadiusBig / 2f * mFraction * 3f;//0到1 的三分之一 用来给大圆做效果;
   mPointData[4] = mRadiusBig - mRadiusSmall / 2f * mFraction * 3f;
   mPointData[5] = 0;
   mPointData[6] = mPointData[2];
   mPointData[7] = -mPointData[3];
   mPointCtrl[0] = mPointData[0];// x轴一样
   mPointCtrl[1] = mRadiusBig * C;// y轴向下的
   mPointCtrl[2] = mPointData[2] - mRadiusBig * C;
   mPointCtrl[3] = mPointData[3];// y轴一样
   mPointCtrl[4] = mPointData[2] + mRadiusBig * C;
   mPointCtrl[5] = mPointData[3];
   mPointCtrl[6] = mPointData[4];
   mPointCtrl[7] = mPointCtrl[1];
   mPointCtrl[8] = mPointData[4];
   mPointCtrl[9] = -mPointCtrl[1];
   mPointCtrl[10] = mPointCtrl[4];
   mPointCtrl[11] = mPointData[7];
   mPointCtrl[12] = mPointCtrl[2];
   mPointCtrl[13] = mPointData[7];
   mPointCtrl[14] = mPointData[0];
   mPointCtrl[15] = -mPointCtrl[1];
   mPathCircle.reset();
   mPathCircle.moveTo(mPointData[0], mPointData[1]);
   mPathCircle.cubicTo(mPointCtrl[0], mPointCtrl[1], mPointCtrl[2], mPointCtrl[3], mPointData[2], mPointData[3]);
   mPathCircle.cubicTo(mPointCtrl[4], mPointCtrl[5], mPointCtrl[6], mPointCtrl[7], mPointData[4], mPointData[5]);
   mPathCircle.cubicTo(mPointCtrl[8], mPointCtrl[9], mPointCtrl[10], mPointCtrl[11], mPointData[6], mPointData[7]);
   mPathCircle.cubicTo(mPointCtrl[12], mPointCtrl[13], mPointCtrl[14], mPointCtrl[15], mPointData[0], mPointData[1]);
 }

// mFraction 1/3 ~ 3/4
 private void setBezierPath2() {
   mPointData[0] = -mRadiusSmall / 2 - (mFraction - 1 / 3f) * mRadiusBig * 2f;
   if (mFraction < 2 / 3f) {
     mPointData[1] = -mRadiusSmall;
   } else {
     mPointData[1] = -mRadiusSmall + (mFraction - 2 / 3f) * 3 * mRadiusSmall;
   }
   if (mFraction < 3 / 4f) {
     mPointData[2] = 0;
   } else {
     //当分裂超过一定程度让结束点的位置变远
     mPointData[2] = (mFraction - 3 / 4f) * 16 * mPointData[0];
   }
   //当动画执行进度大于2/3时,此时该点接近于0
   mPointData[3] = -mRadiusBig + mFraction * mRadiusBig * 1.5f < -0.01f * mRadiusBig ? -mRadiusBig + mFraction * mRadiusBig * 1.5f : 0.01f * -mRadiusBig;

mPointData[4] = mPointData[2];
   mPointData[5] = -mPointData[3];

mPointData[6] = mPointData[0];
   mPointData[7] = -mPointData[1];

mPointCtrl[0] = mPointData[0] + mRadiusSmall;
   mPointCtrl[1] = mPointData[3];

mPointCtrl[2] = mPointData[0] + mRadiusSmall;
   mPointCtrl[3] = -mPointData[3];

mPathBezier.reset();
   mPathBezier.moveTo(mPointData[0], mPointData[1]);
   mPathBezier.quadTo(mPointCtrl[0], mPointCtrl[1], mPointData[2], mPointData[3]);
   mPathBezier.lineTo(mPointData[4], mPointData[5]);
   mPathBezier.quadTo(mPointCtrl[2], mPointCtrl[3], mPointData[6], mPointData[7]);

mPathBezier2.reset();
   mPathBezier2.moveTo(-mPointData[0], mPointData[1]);
   mPathBezier2.quadTo(-mPointCtrl[0], mPointCtrl[1], -mPointData[2], mPointData[3]);
   mPathBezier2.lineTo(-mPointData[4], mPointData[5]);
   mPathBezier2.quadTo(-mPointCtrl[2], mPointCtrl[3], -mPointData[6], mPointData[7]);

}

// mFraction 1/3 ~ 3/4
 private void setBezierPath() {
   mPathBezier.reset();
   float distance = (2 * mRadiusSmall + mRadiusSmall / 2f) * mFraction;
   //float topY = mRadiusSmall * (1 - 0.6f * mFraction);
   float topY = mRadiusSmall - mRadiusSmall * (mFraction - 1 / 3f);
   float distanceBezier = topY - distance * C * (0.5f + 0.5f * mFraction);
   if (mDistanceBezier != 0 && distanceBezier < (mDistanceBezier)) {
     distanceBezier = mDistanceBezier;
   }
   mPathBezier.moveTo(-distance, topY);
   mPathBezier.cubicTo(-distance, distanceBezier, distance, distanceBezier, distance, topY);
   if (mDistanceBezier == 0) {
     mPathMeasure.setPath(mPathBezier, false);
     mLength = mPathMeasure.getLength();
     mPathMeasure.getPosTan(mLength / 2, mPos, null);
     if (mPos[1] <= 8) {
       mDistanceBezier = distanceBezier;
       mPathBezier.reset();
       mPathBezier.moveTo(-distance, topY);
       mPathBezier.cubicTo(-distance, mDistanceBezier, distance, mDistanceBezier, distance, topY);
       mPathBezier.lineTo(distance, -topY);
       mPathBezier.cubicTo(distance, -mDistanceBezier, -distance, -mDistanceBezier, -distance, -topY);
       mPathBezier.close();
       return;
     }
   }
   mPathBezier.lineTo(distance, -topY);
   mPathBezier.cubicTo(distance, -distanceBezier, -distance, -distanceBezier, -distance, -topY);
   mPathBezier.close();
 }

// mFraction 3/4 ~ 1
 private void setLastBezierPath() {
   float x = -mRadiusSmall / 2f - (mFraction - 1 / 3f) / (2 / 3f) * (mRadiusSmall * 2 + mRadiusSmall / 2f);
   mPathBezier.reset();
   mPathBezier.moveTo(x, mRadiusSmall);
   mPathBezier.quadTo(x, 0, x + mRadiusSmall + mRadiusSmall * (4 - mFraction * 4), 0);
   mPathBezier.quadTo(x, 0, x, -mRadiusSmall);
   mPathBezier.lineTo(x, mRadiusSmall);
   mPathBezier.moveTo(-x, mRadiusSmall);
   mPathBezier.quadTo(-x, 0, -x - mRadiusSmall - mRadiusSmall * (4 - mFraction * 4), 0);
   mPathBezier.quadTo(-x, 0, -x, -mRadiusSmall);
   mPathBezier.lineTo(-x, mRadiusSmall);
   mPathBezier.close();
 }

@Override
 protected void onAttachedToWindow() {
   super.onAttachedToWindow();
   if (!mValueAnimator.isRunning())
     mValueAnimator.start();
 }

@Override
 protected void onDetachedFromWindow() {
   super.onDetachedFromWindow();
   if (mValueAnimator.isRunning())
     mValueAnimator.cancel();
 }
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

来源:http://blog.csdn.net/l540675759/article/details/58605213

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com