PathMeasure 轨迹动画神器
来源:互联网 发布:putty串口发送数据 编辑:程序博客网 时间:2024/05/16 06:31
PathMeasure 轨迹动画神器
轨迹动画一般利用SVG来实现,或者使用属性动画,自定义估计值,根据两点之间的线性关系式计算坐标(复杂)
但是使用PathMeasure来进行绘制轨迹动画,so easy。
先看效果:
效果分析:
1、圆圈变成圆弧
2、圆弧不断的变小
实现
方式1:通过不断改变绘制圆弧的开始角度。 这个方法肯定是最先想到的方法,
因为api
drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint)用的最多
方式2:利用PathMeasure,不断截取路径。截取不断变小的圆弧,然后把截取到的path绘制出来就可以。
比较:两种方式相比PathMeasure并没有多大的优势,但是绘制圆弧可以使用 drawArc 来改变角度实现类似轨迹运动的效果,但是,如果path、不是一个圆形,问题就复杂了,PathMeasure的强大也就体现出来了。
学习PathMeasure的使用
查看源码,发现只有100多行,其实也就几个方法。但是确实非常流弊
1、setPath(Path path, boolean forceClosed)
要对path进行操作,首先需要设置一个path,也可以在构造函数中绑定,关键在第二个参数,是否关闭,如果是true,那么会给参数path,强制首尾闭合,因为path可能是一个非闭合的路径。
2、getLength
得到path路径的长度,很有用。
3、getPosTan(float distance, float pos[], float tan[])
得到distance,位置的点的坐标,和在个点与path进行切线的正切,分别放在pos[] 和tan[]中。
例如,distance==length/2,就是得到这条path路径的中间点,的坐标和正切
4、getMatrix(float distance, Matrix matrix, int flags)
这个就厉害了,直接返回得到矩阵,这个矩阵就包含了这个点移动的位置,和倾斜的角度,可以直接在这个点绘制
一个图片,利用这个matrix
5、getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
从开始的距离,到结束的距离,截取一段path放在dst中,
startWithMoveTo 如果为true,这个dst会从截取点开始
如果为false,这个dst会从(0,0)作为开始点
使用PathMeasure来进行分析
接着圆弧动画完了,就是手柄的动画。把属性动画的0到1拆分,0到0.8,完成画的轨迹动画,0.8到1完成手柄的动画,手柄就是一条直线,再使用PathMeasure有点大炮打蚊子,直接不断改变手柄直线的结束点就可以了。
下面是具体的代码实现:
package com.lmj.searchview;import android.animation.Animator;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PathMeasure;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by limengjie * on 2017/5/25.14:37 */public class SearchView extends View { private Matrix mMatrix; private Paint mPaint; private Path mPath; private int mCircleX; private int mCircleY; private int mRadius = 50;//圆的半径 private PathMeasure mPathMeasure; private float circleLength; private float startD; private float endD; private ValueAnimator mvalueAnimator_rotate; private ValueAnimator mvalueAnimator_path; private String Tag = "SearchView"; private Path dstCircle; private int lineD = 0; private static final int Status_Normal = 1;//正常状态 private static final int Status_PathAni = 2;//正在进行轨迹动画 private static final int Status_RotateAni = 3;//正在旋转动画 private int Status = Status_Normal; private int rotateNum; public SearchView(Context context) { this(context, null); init(); } public SearchView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private void init() { mMatrix = new Matrix(); mPaint = new Paint(); mPath = new Path(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.GREEN); mPaint.setStrokeWidth(2); mPaint.setAntiAlias(true); mPathMeasure = new PathMeasure(); dstCircle = new Path(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPath.reset(); mCircleX = getWidth() / 2; mCircleY = getHeight() / 2; switch (Status) { case Status_Normal: drawNormal(canvas); break; case Status_PathAni: drawPathAni(canvas); break; case Status_RotateAni: drawRotateAni(canvas); break; } } private void drawNormal(Canvas canvas) { mPaint.setColor(Color.GREEN); mPath.addCircle(mCircleX, mCircleY, mRadius, Path.Direction.CW); canvas.save(); canvas.drawPath(mPath, mPaint); canvas.rotate(45, mCircleX, mCircleY); canvas.drawLine(mCircleX + mRadius, mCircleY, mCircleX + mRadius * 2, mCircleY, mPaint); canvas.restore(); } private void drawPathAni(Canvas canvas) { mPath.addCircle(mCircleX, mCircleY, mRadius, Path.Direction.CW); canvas.save(); canvas.rotate(45, mCircleX, mCircleY); mPathMeasure.setPath(mPath, false); circleLength = mPathMeasure.getLength(); endD = circleLength; dstCircle.reset(); Log.i(Tag, "sd:" + startD + ",ed:" + endD); mPathMeasure.getSegment(startD, endD, dstCircle, true);// mPaint.setColor(Color.RED); canvas.drawPath(dstCircle, mPaint);//圆的动画 canvas.drawLine(mCircleX + mRadius, mCircleY, mCircleX + mRadius * 2 - lineD, mCircleY, mPaint); canvas.restore(); } private void drawRotateAni(Canvas canvas) { mPath.addCircle(mCircleX, mCircleY, mRadius, Path.Direction.CW); canvas.save(); canvas.rotate(45+rotateNum, mCircleX, mCircleY); mPathMeasure.setPath(mPath, false); circleLength = mPathMeasure.getLength(); dstCircle.reset(); mPathMeasure.getSegment(0, 20, dstCircle, true);// mPaint.setColor(Color.RED); canvas.drawPath(dstCircle, mPaint);//圆的动画 } private void startPathAni() {// if(null==mvalueAnimator){ if (null != mvalueAnimator_path) { mvalueAnimator_path.cancel(); } lineD = 0; mvalueAnimator_path = ValueAnimator.ofFloat(0, 1); mvalueAnimator_path.setDuration(1500); mvalueAnimator_path.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //0-0.8,分割圆,0.8到1 分割手柄 float fraction = animation.getAnimatedFraction(); if (fraction <= 0.8f) { startD = circleLength * fraction / 0.7f; } else { startD = circleLength; lineD = (int) ((fraction - 0.8) * mRadius / 0.2f); } if(fraction==1f){ lineD = mRadius; Status = Status_RotateAni; postInvalidate(); startRotateAni(); } postInvalidate(); } }); mvalueAnimator_path.start();// } } public void startRotateAni(){ if (null != mvalueAnimator_rotate) { mvalueAnimator_rotate.cancel(); } mvalueAnimator_rotate = ValueAnimator.ofInt(0, 360,0); mvalueAnimator_rotate.setDuration(3000); mvalueAnimator_rotate.setRepeatMode(ValueAnimator.RESTART); mvalueAnimator_rotate.setRepeatCount(-1); mvalueAnimator_rotate.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { rotateNum = (int) animation.getAnimatedValue(); postInvalidate(); } }); mvalueAnimator_rotate.start(); } public void startSearch() { Status = Status_PathAni; startPathAni(); postInvalidate(); } public void stopSearch() { Status = Status_Normal; if (null != mvalueAnimator_rotate) { mvalueAnimator_rotate.cancel(); } if (null != mvalueAnimator_path) { mvalueAnimator_path.cancel(); } postInvalidate(); }}
总结,
只要我们有一个已知的path,就可以通过PathMeasure,来截取轨迹,根据距离来获取某个点的坐标和正切,所以有了path,轨迹动画就so easy了。
源码下载
- PathMeasure 轨迹动画神器
- 使用PathMeasure实现 动画CheckBox
- 使用PathMeasure制作Loading动画
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- Android使用PathMeasure实现加载动画
- PathMeasure 仿支付宝支付动画
- OGRE轨迹动画
- 使用动画轨迹
- Ogre轨迹动画
- Android 椭圆轨迹动画
- 安卓开发之使用PathMeasure自定义加载动画控件
- Android:使用PathMeasure绘制动画效果的搜索按钮
- 轨迹动画演示的例子
- jQuery模拟抛物线轨迹动画
- C语音基础-指针初识15
- 成功的 Git 分支模型
- php7安装zendopcache
- jquery打印插件
- 关于tomcat开机自启
- PathMeasure 轨迹动画神器
- 网桥的配置
- windows 下安装python numpy和scipy的一些问题
- PHP之include_once与require_once的区别
- iOS 10系统的一个严重Bug
- 设计模式 ——接口适配
- js如何判断一个对象{}是否为空对象,没有任何属性
- Linux压缩解压命令
- Apache Tomcat7.0 安装流程