您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關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) {// 畫貝塞爾曲線 setBezierPath3(); 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 setBezierPath3() { 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(); } }
感謝各位的閱讀!關于“Android動畫之小球擬合動畫的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。