什麼是貝塞爾曲線(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-貝塞爾曲線》 《三次貝塞爾曲線練習之彈性的圓》