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,mRadiusSmall,Path.Direction.CW); mPathCircle.addCircle(mRadiusSmall / 2f * mFraction * 3,Path.Direction.CW); } else { float distance = (mFraction - 1 / 3f) / (2 / 3f) * (mRadiusSmall * 2 + mRadiusSmall / 2f); mPathCircle.addCircle(-mRadiusSmall / 2f - distance,Path.Direction.CW); mPathCircle.addCircle(mRadiusSmall / 2f + distance,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],mPointData[3]); mPathBezIEr.lineto(mPointData[4],mPointData[5]); mPathBezIEr.quadTo(mPointCtrl[2],mPointData[7]); mPathBezIEr2.reset(); mPathBezIEr2.moveto(-mPointData[0],mPointData[1]); mPathBezIEr2.quadTo(-mPointCtrl[0],-mPointData[2],mPointData[3]); mPathBezIEr2.lineto(-mPointData[4],mPointData[5]); mPathBezIEr2.quadTo(-mPointCtrl[2],-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,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,topY); mPathBezIEr.lineto(distance,-topY); mPathBezIEr.cubicTo(distance,-mdistanceBezIEr,-distance,-topY); mPathBezIEr.close(); return; } } mPathBezIEr.lineto(distance,-topY); mPathBezIEr.cubicTo(distance,-distanceBezIEr,-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,x + mRadiusSmall + mRadiusSmall * (4 - mFraction * 4),0); mPathBezIEr.quadTo(x,x,-mRadiusSmall); mPathBezIEr.lineto(x,mRadiusSmall); mPathBezIEr.moveto(-x,mRadiusSmall); mPathBezIEr.quadTo(-x,-x - mRadiusSmall - mRadiusSmall * (4 - mFraction * 4),0); mPathBezIEr.quadTo(-x,-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(); }}
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
总结以上是内存溢出为你收集整理的Android动画之小球拟合动画实例全部内容,希望文章能够帮你解决Android动画之小球拟合动画实例所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)