当前位置:网站首页>PathMeasure 轨迹动画神器
PathMeasure 轨迹动画神器
2022-08-09 14:55:00 【按劳分配】
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了。
边栏推荐
- Basic principles and common methods of digital image processing
- How to List < Map> grouping numerical merge sort
- 如何选择可靠的亚马逊代运营
- 如何保证电脑硬盘格式化后数据不能被恢复?
- Talking about quantitative trading and programmatic trading
- Suddenly want to analyze the mortgage interest rate and interest calculation
- WebGL:BabylonJS入门——初探:数据展示
- For programming trading, focusing on forecast or on countermeasures?
- 回收站一直显示未清空的图标问题
- Different compilers, different modes, impact on results
猜你喜欢
随机推荐
回收站一直显示未清空的图标问题
小型项目如何使用异步任务管理器实现不同业务间的解耦
为什么要学编译原理
What do professional quantitative traders think about quantitative trading?
SNR signal-to-noise ratio
微信小程序tabs
升职加薪之SQL索引
Noun concept summary (not regularly updated ~ ~)
At the beginning of the C language order 】 【 o least common multiple of three methods
Qt对话框中show和exec的区别
如何保证电脑硬盘格式化后数据不能被恢复?
爱因斯坦的光子理论
How to make your quantitative trading system have probabilistic advantages and positive return expectations?
Arduino 飞鼠 空中鼠标 陀螺仪体感鼠标
keras使用class weight和sample weight处理不平衡问题
Servlet life cycle
LNK1123:转换到COFF期间失败:文件无效或损坏
Suddenly want to analyze the mortgage interest rate and interest calculation
ImageWatch无法显示图像
[Mysql]--Transaction, transaction isolation level, dirty read, non-repeatable read, phantom read analysis









