当前位置:网站首页>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了。
边栏推荐
- Example of file operations - downloading and merging streaming video files
- How to List < Map> grouping numerical merge sort
- Sequelize配置中的timezone测试
- WebGL:BabylonJS入门——初探:数据展示
- 大数组减小数组常用方法
- How to create a new project with VS+Qt
- 名词概念总结(不定期更新~~)
- Noun concept summary (not regularly updated ~ ~)
- 回收站一直显示未清空的图标问题
- OpenCV简介与搭建使用环境
猜你喜欢
随机推荐
Analysis of the common methods and scopes of the three servlet containers
大数组减小数组常用方法
【C语言初阶】倒置字符串(输入 I like beijing. 输出beijing. like I)
js总结,基础篇
小型项目如何使用异步任务管理器实现不同业务间的解耦
How to make your quantitative trading system have probabilistic advantages and positive return expectations?
What are the misunderstandings about the programmatic trading system interface?
ASP.Net Core实战——身份认证(JWT鉴权)
cheerio根据多个class匹配
九、【Vue-Router】缓存路由组件 keep-alive标签
How can I know if quantitative programmatic trading is effective?
走得通,看得见!你的交通“好帮手”
Talking about quantitative trading and programmatic trading
浏览器中的302你真的知道吗
ASP.Net Core实战——使用Swagger
How to achieve stable profit through the stock quantitative trading interface?
Left-handed and Right-handed Binary Sorted Trees
What do professional quantitative traders think about quantitative trading?
WebGL探索——抉择:实践方向(twgl.js、Filament、Claygl、BabylonJS、ThreeJS、LayaboxJS、SceneJS、ThinkJS、ThingJS)
More than pytorch from zero to build neural network to realize classification (training data sets)








