什么是贝塞尔曲线(Bézier曲线)?
贝塞尔曲线,又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。贝赛尔曲线是电脑图形学中相当重要的参数曲线。可以这么说基本上任何的曲线和曲面都可以用贝塞尔公式来解决。
贝塞尔曲线分为一阶贝塞尔曲线、二阶贝塞尔曲线、三阶贝塞尔曲线.......n阶贝塞尔曲线,而一阶贝塞尔曲线就是一条直线,在Android中可以通过Path的quadTo方法和cubicTo方法分别绘制出二阶贝塞尔曲线和三阶贝塞尔曲线。
一阶贝塞尔曲线
一阶贝塞尔曲线没有控制点,仅有两个数据点,也就是一条直线
二阶贝塞尔曲线
二阶贝塞尔曲线仅有一个控制点,其表示公式:
二阶贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪,P0和P2是曲线的始点和终点,P1是控制点。
我们看下t的变化:
t的变化直接影响到曲线的弧度。绘制二阶贝塞尔曲线在Android中使用Path提供的quadTo方法绘制。
三阶贝塞尔曲线
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。
Android中绘制三阶的贝塞尔曲线,对应Path的cubicTo方法。
N阶贝塞尔曲线
N阶贝塞尔曲线更加复杂,其表示的公式如下:
如上公式可如下递归表达: 用表示由点P0、P1、…、Pn所决定的贝兹曲线。
动态演示过程:
看了以上的公式,是不是已经头晕脑胀了,没关系,能够看明白贝塞尔曲线的演示过程就行了。如果还不是很明白,推荐可以The Bézier Game来玩一下,也可以参考《贝塞尔曲线扫盲》这篇文章,里面的图形讲解更加形象。 那么贝塞尔曲线有什么用呢?贝塞尔曲线在平时的APP中随处可见,如:qq的提示红点拖拽时的粘性过程、直播中的爱心的浮动路径、手机充电时的图形等等。
关于贝塞尔去曲线的应用,网上搜索以下一大堆demo,本文看一个简单的应用:模仿水波上涨的情况。
public class WaveView extends View {
private Paint paint;
private Path path;
private int waveLength = 800;
private int dx;
private int dy;
public WaveView(Context context) {
this(context, null);
init();
}
public WaveView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint();
paint.setColor(0xff84C1ff);
paint.setStyle(Style.FILL_AND_STROKE);
path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.reset();
int originY = 1000;
if(dy<originY + 150){
dy += 5;
}
int halfWaveLength = waveLength/2;
path.moveTo(-waveLength+dx, originY-dy);
//屏幕的宽度里面放多少个波长
for (int i = -waveLength; i < getWidth() + waveLength; i += waveLength) {
//相对绘制二阶贝塞尔曲线(相对于自己的起始点--也即是上一个曲线的终点 的距离dx1)
path.rQuadTo(halfWaveLength/2, -150, halfWaveLength, 0);
path.rQuadTo(halfWaveLength/2, 150, halfWaveLength, 0);
}
//颜色填充
//画一个封闭的空间
path.lineTo(getWidth(), getHeight());
path.lineTo(0, getHeight());
path.close();
canvas.drawPath(path, paint);
}
public void startAnimation(){
ValueAnimator animator = ValueAnimator.ofInt(0,waveLength);
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
//无限循环
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
}
复制代码
使用rQuadTo绘制二阶的贝塞尔曲线,调用两次,一个绘制波峰,一个绘制波谷。然后使用动画来改变dx和dy来模拟上涨和水波运动的情况。
上面用到的动画来改变dx,也可以使用以下写法:
private class MoveAnimation extends Animation {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
mInterpolatedTime = interpolatedTime;
Log.i("lwj","mInterpolatedTime:"+mInterpolatedTime);
invalidate();
}
}
public void startAnimation() {
mPath.reset();
mInterpolatedTime = 0;
MoveAnimation move = new MoveAnimation();
move.setDuration(1000);
move.setInterpolator(new AccelerateDecelerateInterpolator());
// move.setRepeatCount(Animation.INFINITE);
// move.setRepeatMode(Animation.REVERSE);
startAnimation(move);
}
复制代码
这里拿到的数值mInterpolatedTime范围式[0,1]。
使用:
public class GallaryHorizonalScrollViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WaveView view = new WaveView(this);
setContentView(view);
view.startAnimation();
}
}
复制代码
关于贝塞尔曲线的介绍和使用,大家可以参考: 《Android 自定义View高级特效,神奇的贝塞尔曲线》 《Android-贝塞尔曲线》 《三次贝塞尔曲线练习之弹性的圆》