当前位置: 移动技术网 > 移动技术>移动开发>Android > Android动画之小球拟合动画实例

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

2019年07月24日  | 移动技术网移动技术  | 我要评论

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();
  }
}


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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网