爲何要講貝塞爾曲線,實際上 Android 中不少效果都有用到貝塞爾曲線。html
QQ空間 直播頁面右下角的禮物冒泡特效 android
水流波動效果git
圖片或書本翻頁效果github
能夠先對貝塞爾曲線有一個大概的認識。canvas
貝塞爾曲線維基百科api
貝塞爾曲線的數學基礎是早在 1912 年就廣爲人知的 伯恩斯坦多項式 。但直到 1959 年,當時就任於雪鐵龍的法國數學家 Paul de Casteljau 纔開始對它進行圖形化應用的嘗試,並提出了一種數值穩定的 de Casteljau 算法。然而貝塞爾曲線的得名,倒是因爲 1962 年另外一位就任於雷諾的法國工程師 Pierre Bézier 的普遍宣傳。他使用這種只須要不多的控制點就可以生成複雜平滑曲線的方法,來輔助汽車車體的工業設計。函數
正是由於控制簡便卻具備極強的描述能力,貝塞爾曲線在工業設計領域迅速獲得了普遍的應用。不只如此,在計算機圖形學領域,尤爲是矢量圖形學,貝塞爾曲線也佔有重要的地位。今天咱們最多見的一些矢量繪圖軟件,如 Flash、Illustrator、CorelDraw 等,無一例外都提供了繪製貝塞爾曲線的功能。甚至像 Photoshop 這樣的位圖編輯軟件,也把貝塞爾曲線做爲僅有的矢量繪製工具(鋼筆工具)包含其中。工具
貝塞爾曲線在 Web 開發領域一樣佔有一席之地。CSS3 新增了 transition-timing-function 屬性,它的取值就能夠設置爲一個三次貝塞爾曲線方程。在此以前,也有很多 JavaScript 動畫庫使用貝塞爾曲線來實現美觀逼真的緩動效果。
線性貝塞爾曲線
給定點P0、P1,線性貝塞爾曲線只是一條兩點之間的直線。這條線由下式給出:
二次方貝塞爾曲線
二次方貝塞爾曲線的路徑由給定點P0、P一、P2的函數B(t)追蹤:
三次方貝塞爾曲線
P0、P一、P二、P3 四個點在平面或在三維空間中定義了三次方貝塞爾曲線。曲線起始於 P0 走向 P1 ,並從 P2 的方向來到 P3 。通常不會通過 P1 或 P2 ;這兩個點只是在那裏提供方向資訊。 P0 和 P1 之間的間距,決定了曲線在轉而趨進 P2 以前,走向 P1 方向的「長度有多長」。
曲線的參數形式爲:
n階貝塞爾曲線
n階貝塞爾曲線可以下判斷,給定點P0、p一、...、Pn,其貝塞爾曲線即
看公式仍是有些抽象,接下來能夠看一下圖解,具像的瞭解一下什麼是貝塞爾曲線:以二階貝塞爾曲線和三階貝塞爾曲線爲例。
有一篇文章寫的極好
貝塞爾曲線不只能畫直線,也能畫曲線。即使是更復雜的曲線,控制點的增長也只是線性的。這一特色使其不光在工業設計領域大展拳腳,就連數學基礎很差的人也能夠比較容易地掌握,好比大多數平面美術設計師們。
簡單來講,貝塞爾曲線就是將任意一條曲線轉化爲準確的數學公式。 Bezier 曲線是用一系列點來控制曲線狀態的。
一些關鍵的名詞
在 Android 開發中,咱們只考慮二階貝塞爾曲線和三階貝塞爾曲線, SDK 也是隻提供了二階和三階的 API 調用。
固然,對於再高階的貝塞爾曲線,一般能夠拆分紅多個低階的貝塞爾曲線。
推薦一個好的貝塞爾曲線的動態演示,能夠很直觀的感覺一下:
myst729.github.io/bezier-curv…
這裏推薦 醫生 的一片博文,已經很是全面的介紹了貝塞爾曲線在 Android 中的一些用法及實例,寫的很好。
下面的內容都基於這篇文章。
quadTo 是基於絕對座標,而 rQuadTo 是基於相對座標。
mPath.moveTo(mStartPointX, mStartPointY);
//二階貝塞爾曲線
mPath.quadTo(mAuxiliaryX, mAuxiliaryY, mEndPointX, mEndPointY);
canvas.drawPath(mPath, mPaintBezier);複製代碼
這兩個API在原理上也是能夠互相轉換的。
兩個點的話涉及到多點觸摸,有興趣能夠看看下面這篇文章。
cubicTo是基於絕對座標,而rCubicTo是基於相對座標。
mPath.moveTo(mStartPointX, mStartPointY);
// 三階貝塞爾曲線
mPath.cubicTo(mAuxiliaryOneX, mAuxiliaryOneY, mAuxiliaryTwoX, mAuxiliaryTwoY, mEndPointX, mEndPointY);
canvas.drawPath(mPath, mPaintBezier);複製代碼
三階貝塞爾曲線其實也沒什麼,就是多了一個控制點,記錄下他的位置就能夠了。
當在屏幕上繪製路徑的時候,咱們一般會經過 Path.lineTo 將各個觸點鏈接起來,可是這樣確定會很生硬。由於是經過直線來鏈接的。若是經過二階貝塞爾曲線,就會圓滑不少。不會出現太多的生硬鏈接。
if (dx >= offset || dy >= offset) {
// 貝塞爾曲線的控制點爲起點和終點的中點
float cX = (x1 + preX) / 2;
float cY = (y1 + preY) / 2;
mPath.quadTo(preX, preY, cX, cY);
// mPath.lineTo(x1, y1);
mX = x1;
mY = y1;
}複製代碼
經過紀錄 Move 過程當中點位置的變化,而後傳入 quadTo 中的參數,就能夠實現圓滑的繪圖。
根據公式咱們能夠模仿着寫一個工具類:
public class BezierUtil {
/** * B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1] * * @param t 曲線長度比例 * @param p0 起始點 * @param p1 控制點 * @param p2 終止點 * @return t對應的點 */
public static PointF CalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
PointF point = new PointF();
float temp = 1 - t;
point.x = temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x;
point.y = temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y;
return point;
}
/** * B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3, t ∈ [0,1] * * @param t 曲線長度比例 * @param p0 起始點 * @param p1 控制點1 * @param p2 控制點2 * @param p3 終止點 * @return t對應的點 */
public static PointF CalculateBezierPointForCubic(float t, PointF p0, PointF p1, PointF p2, PointF p3) {
PointF pointF = new PointF();
float temp = 1- t;
pointF.x = p0.x * temp * temp * temp + 3 * p1.x * t * temp * temp + 3 * p2.x * t * t * temp + p3.x * t * t * t;
pointF.y = p0.y * temp * temp * temp + 3 * p1.y * t * temp * temp + 3 * p2.y * t * t * temp + p3.y * t * t * t;
return pointF;
}
}複製代碼
github.com/venshine/Be… ( 經過 de Casteljau 算法繪製貝塞爾曲線,並計算它的切線,實現 1-7 階貝塞爾曲線的造成動畫 )