概述
- 在開始本故事的之前,先來介紹下故事的背景。話說幾百年前,從天而降一座神山,遠遠看去像一天光滑的絲帶,它的名字叫做:“貝塞爾曲線"。有大法師預言登上這座神山可以發現天地大秘但是前途艱險。
定義
- 摘自百度百科
貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用於二維圖形應用程序的數學曲線。一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節點組成,節點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。
“貝賽爾曲線”是由法國數學家Pierre Bézier所發明,由此為計算機矢量圖形學奠定了基礎。它的主要意義在於無論是直線或曲線都能在數學上予以描述。
公式
- 由於應用用到主要以二階貝塞爾曲線為主,貼下二階的公式:
二次方公式
二次方貝茲曲線的路徑由給定點P0、P1、P2的函數B(t):
如何應用?
- 為了前往"貝塞爾曲線山",向那些從前登上神山的老前輩請教;
所需的Android知識
-
畫筆(paint),路徑(path),畫布(canvas)類的api要熟悉
-
View繪制的生命周期
簡單來看:測量-measure 擺放-layout 繪制-draw
- Android觸摸事件
這里需要了解onTouchEvent方法可以捕捉到觸屏的事件
用手勢畫光滑的曲線
-
路途艱險,在這里我碰到了大白虎,史前巨獸猛獁象,海天鯤鵬,經歷了生死考驗終於登上神山,恍然大悟,天地大秘原來在此。
-
讓我們想一想畫東西需要什么?答案:一塊白板,一只筆。
-
這里的關鍵是手勢與光滑,處理手勢的話就是前面講的重寫Android觸摸事件,聰明的你一定想到了通過二階貝塞爾曲線去做到光滑。
-
畫一條二階貝塞爾曲線需要3個點,兩個數據點一個控制點,那么手勢落下的點--起始點(x1,y1)與不斷移動的手的觸點是數據點,控制點需要自己創造,那線段的中點是最好計算的,假設第一個手滑動到的點(x2,y2),那么中點就是((x1+x2)/2,(y1+y2)/2)。
-
重寫Android觸摸事件需要捕捉MOVE類型與DOWN類型的事件,DOWN類型的事件中需要記錄起始點的位置,而MOVE類型事件需要緩沖上一次移動的位置。
-
1.聲明控制點,曲線,起始點,以及判定滑動的距離
private Paint controlPaint;
private Path mCurrentPath;
private float startPointX;
private float startPointY;
//畫貝塞爾曲線的標識--可以自定義值
private float offset = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- 2.初始化畫筆與路徑
public PaintBeSaiErView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//建立路徑
mCurrentPath = new Path();
//繪制時抗鋸齒
controlPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//設置畫筆樣式
controlPaint.setStyle(Paint.Style.STROKE);
//設置畫筆的粗細
controlPaint.setStrokeWidth(8);
//設置畫筆顏色
controlPaint.setColor(Color.RED);
}
- 3.重寫onTouchEvent,記錄手勢起始點與移動位置並繪制貝塞爾曲線,通過
invalidate方法
更新UI視圖
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//為了方便測試,每次下落之前清空路徑
mCurrentPath.reset();
float x = event.getX();
float y = event.getY();
startPointX = x;
startPointY = y;
//移動到起始點
mCurrentPath.moveTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float curX= event.getX();
float curY= event.getY();
float preX= startPointX;
float preY= startPointY;
if(Math.abs(preX-curX)>=offset||Math.abs(preX-curY)>=offset) {
mCurrentPath.quadTo((curX + preX) / 2, (curY + preY) / 2, curX, curY);
startPointX = curX;
startPointY = curY;
}
invalidate();
break;
}
return true;
}
- 4.下面對比使用線段畫lineTo(curX, curY)與貝塞爾曲線畫quadTo(avgX,avgY)的效果
左圖為線段畫的,右圖為貝塞爾曲線畫的,看起來更圓潤!why?其實,用線段畫基本上看是一個折線圖,而貝塞爾函數畫是一段段曲線
- 當然,貝塞爾曲線的應用十分廣泛,上面是簡單的例子,后面將講如何應用模擬翻頁。
總結
- 總以為登上神山才是最大的收獲,原來一路走來更有收獲。
- 去了解一個事物的時候,要善於思考,記憶中越來越深刻的,往往思考的越透徹。