前言
贝塞尔曲线于1962由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。现在贝塞尔曲线在计算机图形学领域也是一个相当重要的参数曲线,很多画图工具软件都包含贝塞尔曲线的工具对象。Android开发过程中也可以通过它实现很多有趣的特效动画,这里通过简单的代码编写来深入学习贝塞尔曲线的生成。
曲线生成
一阶贝塞尔曲线
给定点两个点,一阶贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:
接下来使用编程来实现画出这条线,需要在自定义的View控件中包含开始和结束两个点,使用属性动画来做t的生成对象。
public class BezierView extends View {
private Paint mPaint;
// 开始位置
private Point mStart;
// 结束位置
private Point mEnd;
// t是固定值的时候当前点的位置
private Point mCurrent;
private ValueAnimator mValueAnimator;
// 当前t的值
private float mProgress;
public BezierView(Context context) {
this(context, null);
}
public BezierView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BezierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStrokeWidth(10);
mStart = new Point();
mStart.x = 100;
mStart.y = 100;
mEnd = new Point();
mEnd.x = 600;
mEnd.y = 600;
mCurrent = new Point();
// t从0到1变化耗时3秒
mValueAnimator = ValueAnimator.ofFloat(0, 1f);
mValueAnimator.setDuration(3000);
mValueAnimator.addUpdateListener(animation -> {
mProgress = animation.getAnimatedFraction();
// 每次t发生变化就刷新界面
invalidate();
});
mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
post(() -> {
mValueAnimator.start();
});
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 根据当前t的值,获取当前生成的点
mCurrent.x = (int) (mStart.x + mProgress * (mEnd.x - mStart.x));
mCurrent.y = (int) (mStart.y + mProgress * (mEnd.y - mStart.y));
mPaint.setColor(Color.RED);
canvas.drawLine(mStart.x, mStart.y, mCurrent.x, mCurrent.y, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(mCurrent.x, mCurrent.y, 10, mPaint);
mPaint.setColor(Color.GREEN);
canvas.drawLine(mCurrent.x + 8, mCurrent.y + 8, mEnd.x, mEnd.y, mPaint);
}
}
上面红色的线就是一阶贝塞尔曲线生成的直线,这个效果还是比较简单的,涉及到的点相对比较少。
二阶贝塞尔曲线
二阶贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:
如果直接使用这个公式去计算结果然后把所有的点连接起来,也是可以生成贝塞尔曲线的,不过这种明显不够直观,这里还是使用前面生成直线的方式生成一条贝塞尔曲线。
public class Bezier2View extends View {
private Paint mPaint;
// 开始点,控制点和结束点
private Point mStart;
private Point mControl;
private Point mEnd;
// 开始点和控制点之间随t变化生成的点
private Point mCurrent1;
// 控制点和结束点之间随t变化的点
private Point mCurrent2;
private Path mPath;
// 记录上一个t的贝塞尔曲线位置
private Point mLastPoint;
private Point mTmpPoint;
// t的动画生成对象
private ValueAnimator mValueAnimator;