Android 动效中的贝塞尔曲线
1、贝塞尔曲线的基本概念
贝塞尔曲线(Bézier curve)是一种用于计算机图形学的数学曲线,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)发明。它通过控制点来定义曲线的形状,是实现平滑动画效果的重要工具。
工具:
核心概念:
- 控制点:决定曲线形状的关键 points
- 阶数:控制点的数量减一(如3个控制点形成2阶曲线)
- 参数 t:从0到1的参数,用于计算曲线上的点
常见的贝塞尔曲线类型:
- 线性贝塞尔曲线(1阶):由2个控制点定义,形成直线
- 二次贝塞尔曲线(2阶):由3个控制点定义,形成抛物线
- 三次贝塞尔曲线(3阶):由4个控制点定义,形成更复杂的曲线
贝塞尔曲线的特点:
- 曲线始终在控制点构成的凸包内
- 曲线平滑,没有尖锐的转折点
- 可以通过调整控制点精确控制曲线形状
- 计算高效,适合实时动画
2、Android 中贝塞尔曲线的实现
2.1、Path 类的使用
Android 中的 Path 类提供了绘制贝塞尔曲线的方法:
// 二次贝塞尔曲线
public void quadTo(float x1, float y1, float x2, float y2)
// 三次贝塞尔曲线
public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)参数说明:
- quadTo:(x1,y1) 是控制点,(x2,y2) 是终点
- cubicTo:(x1,y1) 和 (x2,y2) 是控制点,(x3,y3) 是终点
2.2、自定义 View 实现
public class BezierView extends View {
private Path mPath;
private Paint mPaint;
private float mStartX, mStartY;
private float mControlX, mControlY;
private float mEndX, mEndY;
public BezierView(Context context) {
super(context);
init();
}
private void init() {
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
// 初始化点的位置
mStartX = 100;
mStartY = 300;
mControlX = 200;
mControlY = 100;
mEndX = 300;
mEndY = 300;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 重置路径
mPath.reset();
// 移动到起点
mPath.moveTo(mStartX, mStartY);
// 绘制二次贝塞尔曲线
mPath.quadTo(mControlX, mControlY, mEndX, mEndY);
// 绘制曲线
canvas.drawPath(mPath, mPaint);
// 绘制控制点和辅助线
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(2);
canvas.drawLine(mStartX, mStartY, mControlX, mControlY, mPaint);
canvas.drawLine(mControlX, mControlY, mEndX, mEndY, mPaint);
// 绘制控制点
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mStartX, mStartY, 5, mPaint);
canvas.drawCircle(mControlX, mControlY, 5, mPaint);
canvas.drawCircle(mEndX, mEndY, 5, mPaint);
}
}2.3、属性动画结合
public class BezierAnimationView extends View {
private Path mPath;
private Paint mPaint;
private float mStartX, mStartY;
private float mEndX, mEndY;
private float mControlX, mControlY;
private float mProgress = 0;
public BezierAnimationView(Context context) {
super(context);
init();
startAnimation();
}
private void init() {
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
mStartX = 100;
mStartY = 300;
mEndX = 500;
mEndY = 300;
}
private void startAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(2000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.addUpdateListener(animation -> {
mProgress = (float) animation.getAnimatedValue();
// 更新控制点位置
mControlX = mStartX + (mEndX - mStartX) * mProgress;
mControlY = mStartY - 200 * mProgress;
invalidate();
});
animator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
mPath.moveTo(mStartX, mStartY);
mPath.quadTo(mControlX, mControlY, mEndX, mEndY);
canvas.drawPath(mPath, mPaint);
}
}3、贝塞尔曲线的实际应用
3.1、平滑的动画效果
// 实现平滑的弹跳效果
public void createBounceAnimation(View view) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(1000);
animator.addUpdateListener(animation -> {
float progress = (float) animation.getAnimatedValue();
// 使用贝塞尔曲线计算位置
float y = calculateBezierY(progress);
view.setTranslationY(y);
});
animator.start();
}
private float calculateBezierY(float t) {
// 二次贝塞尔曲线
float startY = 0;
float controlY = -200;
float endY = 0;
return (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * controlY + t * t * endY;
}3.2、自定义曲线运动路径
// 让 View 沿着贝塞尔曲线运动
public void moveAlongBezier(View view) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(2000);
animator.addUpdateListener(animation -> {
float t = (float) animation.getAnimatedValue();
// 计算曲线上的点
PointF point = calculateBezierPoint(t);
view.setX(point.x);
view.setY(point.y);
});
animator.start();
}
private PointF calculateBezierPoint(float t) {
// 三次贝塞尔曲线
float startX = 100, startY = 100;
float control1X = 200, control1Y = 0;
float control2X = 300, control2Y = 200;
float endX = 400, endY = 100;
float x = (1 - t) * (1 - t) * (1 - t) * startX +
3 * (1 - t) * (1 - t) * t * control1X +
3 * (1 - t) * t * t * control2X +
t * t * t * endX;
float y = (1 - t) * (1 - t) * (1 - t) * startY +
3 * (1 - t) * (1 - t) * t * control1Y +
3 * (1 - t) * t * t * control2Y +
t * t * t * endY;
return new PointF(x, y);
}3.3、高级应用
- 波浪效果:使用多条贝塞尔曲线实现波浪动画
- 平滑过渡:在页面切换时使用贝塞尔曲线实现平滑过渡
- 自定义控件:如贝塞尔曲线实现的进度条、滑块等
- 粒子效果:使用贝塞尔曲线控制粒子的运动轨迹
3.4、最佳实践
- 选择合适的贝塞尔曲线阶数:一般二次或三次曲线足够满足大多数需求
- 合理设置控制点:控制点的位置直接影响曲线的形状
- 结合属性动画:使用 ValueAnimator 控制参数 t 的变化
- 性能优化:在复杂动画中,考虑使用硬件加速和缓存
- 测试不同设备:确保在不同屏幕尺寸和密度下的效果一致
贝塞尔曲线是 Android 动效中非常重要的工具,通过合理使用贝塞尔曲线,可以创建出平滑、自然的动画效果。掌握贝塞尔曲线的基本原理和实现方法,对于 Android 开发者来说,是提升应用用户体验的重要技能之一。

