Android动画之小球拟合动画实例
程序员文章站
2023-08-25 12:33:50
android动画之小球拟合动画实例
实现效果:
动画组成:
1.通过三阶贝塞尔曲线来拟合圆,拟合系数的由来,以及怎么选控制点.
2.利用画布canvas....
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(); } }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!