二阶贝塞尔曲线 rQuadTo 函数实现波浪动画
上篇博客 ”小动画之“绘画板”“ 中讲述了 二阶贝塞尔曲线以及,quadTo() 函数;本文将实现上图动画。
1. rQuadTo() 函数
-
函数
publiC void rQuadTo(float dxl , float dyl, float dx2, float dy2);
-
参数 - 这4 个参数都是 相对值,即相对上一个终点的位移值。
• dxl : 控制点x 坐标,相对上一个终点 x 坐标的位移坐标。可为负值,正值表示相加,负值表示相减。
• dyl :控制点 y 坐标,相对上一个终点 y 坐标的位移坐标。可为负值,正值表示相加,负值表示相减。
• dx2 : 终点 x 坐标,相对上一个终点x 坐标的位移值。可为负值,正值表示相加,负值表示相减。
• d y2 : 终点y 坐标,相对上一个终点y 坐标的位移值。可为负值,正值表示相加, 负值表示相减。
2. 原理与代码实现
2.1 实现全屏波纹
-
在构造函数中,初始化一些必要的变量。
public class AnimWaveView extends View { private Paint mPaint; private Paint mCirPaint;//绘制 外层圆形 private Path mPath; private int mWaveLength = 800;//单个波纹长度(水平方向) private int dx;//波纹水平移动距离 private int originY = 850;//波纹的原始Y坐标 public AnimWaveView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mPath = new Path(); mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL);//波纹设置为填充模式。 mPaint.setColor(0XFF3DDC84); mCirPaint = new Paint(); mCirPaint.setStyle(Paint.Style.STROKE);//外层圆设置为圈圈 mCirPaint.setStrokeWidth(5); mCirPaint.setColor(0XFF3DDC84); setLayerType(View.LAYER_TYPE_SOFTWARE , null); startAnim();//开启波纹动画 } }
-
在 onDraw() 函数中整屏画满波形
@Override protected void onDraw(Canvas canvas) { //剪裁画布,让动画只出现在圆形内, Path cirPath = new Path(); cirPath.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, Path.Direction.CW); canvas.clipPath(cirPath); super.onDraw(canvas); canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mCirPaint); mPath.reset(); int halfWaveLength = mWaveLength / 2;//这里为什么是一半?请参考上篇”绘画版“博文中 4.2 节 mPath.moveTo(-mWaveLength + dx, originY); for (int i = -mWaveLength; i <= getWidth() + mWaveLength; i += mWaveLength) { mPath.rQuadTo(halfWaveLength / 2, -100, halfWaveLength, 0); mPath.rQuadTo(halfWaveLength / 2, 100, halfWaveLength, 0); } mPath.lineTo(getWidth(), getHeight());//闭合路径,否则将无法绘制波浪颜色 mPath.lineTo(0, getHeight()); mPath.close(); canvas.drawPath(mPath, mPaint); if (originY <= -10) { originY = 850;//重置波纹的纵坐标 } }
2.2 实现动画移动
- 在调用 path.moveTo() 函数的时候,将起始点向右移动即可实现。只要移动一个波长的长度,波纹就会重合,就可以实现无限循环。
private void startAnim() { ValueAnimator animator = ValueAnimator.ofInt(0, mWaveLength); animator.setDuration(1500); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { dx = (int) animation.getAnimatedValue(); originY -= (2 * dx / mWaveLength);//Y坐标也随着X增长 postInvalidate(); } }); animator.start(); }
2.3 布局
-
采用帧布局,将两者重叠
<FrameLayout android:layout_width="300dp" android:layout_height="300dp" android:layout_gravity="center"> <com.example.customview.wave_anim.AnimWaveView android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fff" android:layout_gravity="center" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center" android:background="@drawable/ic_launcher" />
3. 注意
-
难点在于对新知识的不熟悉;如何将波纹显示在圆圈里?
应该用到画布剪裁,clipParh() 方法,而不是单纯的重绘圆形 canvas;
声明:本文整理自《《Android自定义控件开发入门与实战》_启舰》;