欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-08-25 12:33:50
android动画之小球拟合动画实例 实现效果: 动画组成: 1.通过三阶贝塞尔曲线来拟合圆,拟合系数的由来,以及怎么选控制点. 2.利用画布canvas....

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


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