Path绘制动态的贝塞尔曲线、PathMeasure来绘制path动画
程序员文章站
2024-01-14 13:36:46
...
上一篇的波浪曲线是左右重复平移,这次是每一帧绘制一条线,组成上下浮动的曲线,下面是效果图
public WaveView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(6);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(200, 200, 90, paint);
path.reset();
path.moveTo(waveLength / 2, startY);
path.rQuadTo(waveLength / 2, -waveHeight, waveLength, 0);
path.rQuadTo(waveLength / 2, waveHeight, waveLength, 0);
canvas.drawPath(path, paint);
下面是通过属性动画来改变贝塞尔控制点
protected void startAnimation() {
//通过属性动画计算高度
final ValueAnimator animator = ValueAnimator.ofInt(-waveHeight, waveHeight);
animator.setDuration(4000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
waveHeight = (int) animation.getAnimatedValue();
postInvalidateDelayed(1);
}
});
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.start();
}
path的曲线动画,下面是效果图
//全部代码如下:
private PathMeasure pathM;
private Bitmap bitmap;
private int bitHeight;
private int bitWidth;
private Rect rect;
private Matrix matrix;
public WaveView(Context context) {
this(context, null);
}
public WaveView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(6);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
//来个path计算器
pathM = new PathMeasure();
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_music);
bitHeight = bitmap.getHeight();
bitWidth = bitmap.getWidth();
}
private Paint paint;//定义画笔
private int waveLength = 200;//定义波长度
private Path path;//曲线路径
private int waveHeight = 120;//定义波的高度
private int startY = 500;//起点的Y坐标
private static String TAG = "test";
private float fraction = 0.0f;
private float[] position = new float[2];
private float[] tange = new float[2];
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(200, 200, 90, paint);
path.reset();
path.moveTo(waveLength / 2, startY);
path.rQuadTo(waveLength / 2, -waveHeight, waveLength, 0);
path.rQuadTo(waveLength / 2, waveHeight, waveLength, 0);
canvas.drawPath(path, paint);
pathM.setPath(path, false);
float length = pathM.getLength();
Log.i(TAG, "WaveView: 长度==" + length);
boolean flag = pathM.getPosTan(fraction * length, position, tange);
if (flag) {
//开始绘制运行轨迹
canvas.drawBitmap(bitmap, position[0] - bitWidth, position[1] - bitHeight, paint);
}
}
protected void computePercent() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0f);
animator.setDuration(4000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
fraction = (float) animation.getAnimatedValue();
Log.i(TAG, "onAnimationUpdate: 百分比==" + fraction);
postInvalidateDelayed(5);
}
});
animator.start();
}
上面有点缺陷就是“音乐符号”的运动角度没有设置和调整,调整后的效果如右边所示:
public WaveView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(6);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
//新增代码。来个path计算器
pathM = new PathMeasure();
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_music);
bitHeight = bitmap.getHeight();
bitWidth = bitmap.getWidth();
matrix=new Matrix();
}
private Paint paint;//定义画笔
private int waveLength = 200;//定义波长度
private Path path;//曲线路径
private int waveHeight = 120;//定义波的高度
private int startY = 500;//起点的Y坐标
private static String TAG = "test";
private float fraction = 0.0f;
private float[] position = new float[2];
private float[] tange = new float[2];
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(200, 200, 90, paint);
path.reset();
path.moveTo(waveLength / 2, startY);
path.rQuadTo(waveLength / 2, -waveHeight, waveLength, 0);
path.rQuadTo(waveLength / 2, waveHeight, waveLength, 0);
canvas.drawPath(path, paint);
pathM.setPath(path, false);
float length = pathM.getLength();
Log.i(TAG, "WaveView: 长度==" + length);
boolean flag = pathM.getPosTan(fraction * length, position, tange);
if (flag) {
//开始绘制运行轨迹
// canvas.drawBitmap(bitmap, position[0] - bitWidth, position[1] - bitHeight, paint);
matrix.reset();
float degree = (float) (Math.atan2(tange[1], tange[0])*180f/Math.PI);
matrix.postRotate(degree,bitmap.getWidth()/2,bitmap.getHeight()/2);
matrix.postTranslate(position[0]-bitmap.getWidth()/2,position[1]-bitmap.getHeight()/2);
canvas.drawBitmap(bitmap,matrix,paint);
}
}
protected void computePercent() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0f);
animator.setDuration(4000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
fraction = (float) animation.getAnimatedValue();
Log.i(TAG, "onAnimationUpdate: 百分比==" + fraction);
postInvalidateDelayed(5);
}
});
animator.start();
}
总结:还有一种比较好的测量 方式,直接返回matrix,这个matrix里面包含了delt x和y,以及tang角度。
boolean flag = pathM.getMatrix(fraction * length, matrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
if (flag) {
canvas.drawBitmap(bitmap, matrix, paint);
}
新测量方式的源码截取和解释如下
/**
* Pins distance to 0 <= distance <= getLength(), and then computes the
* corresponding matrix. Returns false if there is no path, or a zero-length
* path was specified, in which case matrix is unchanged.
*
* @param distance The distance along the associated path
* @param matrix Allocated by the caller, this is set to the transformation
* associated with the position and tangent at the specified distance
* @param flags Specified what aspects should be returned in the matrix.
*/
public boolean getMatrix(float distance, Matrix matrix, int flags) {
return native_getMatrix(native_instance, distance, matrix.native_instance, flags);
}