【乾貨滿滿】貝塞爾曲線(Bézier curve)——什麼神仙操做

話說爲何筆者我要求虐去研究什麼貝塞爾曲線?講真,我一個數學通常般,高數及格飄過的人爲何要求虐去搞數學公式啊!研究完貝塞爾曲線,我忽然想好好學習數學。真的是數學很差,學什麼編程啊。(哭暈在草稿紙中……)編程

正片乾貨在此:bash

科普時間

提到貝塞爾曲線,你們第一反應是什麼?函數

學習CSS的小夥伴應該會知道一個叫作animation-timing-function:cubic-bezier(x1,y1,x2,y2)的參數,用於CSS動畫時間的參數。若是沒法理解,就假象下勻速運動和變速運動的。若是仍是沒感受,就想象你在跑步機上跑步,1小時內,有時用8KM/h的速度,有時候用10KM/h的速度。也就是animation-timing-function:cubic-bezier(x1,y1,x2,y2)的意思就是讓你在必定時間內,用不一樣的速度運動(運動方式不限,能夠是平移,旋轉,拉伸……)。工具

可是貝塞爾曲線,既然是曲線,一開始並非用於時間函數的,而是真的用來畫曲線的,好比PS中的鋼筆工具。(驚喜不驚喜,意外不意外,此處應該有表情包。)學習

PS鋼筆工具(貝塞爾曲線的應用)
PS鋼筆工具(貝塞爾曲線的應用)

Bézier curve的定義

wiki百科定義動畫

A Bézier curve is defined by a set of control points P0 through Pn, where n is called its order (n = 1 for linear, 2 for quadratic, etc.). The first and last control points are always the end points of the curve; however, the intermediate control points (if any) generally do not lie on the curve. The sums in the following sections are to be understood as affine combinations, the coefficients sum to 1.網站

筆者的渣翻譯:一條貝塞爾曲線是由一組Points從P0~PN所控制的,這邊N就是他的順序(好比N=1的時候是線性的,2的時候是二次,等等)。第一個控制點和最後一個控制點是曲線的終點;然而中間的一些控制點(若是有),一般不在曲線上。這些點的組合能夠理解爲仿射組合(affine combination,也就是不只有點,還有點指向的方向),他們的係數之和等於一。ui

通俗解釋:spa

  • 貝塞爾曲線是由一堆點的集合繪製而成。
  • 一堆點是在定義的P0~PN的控制之下得出的。
  • P0~PN這些定義的點,第一個點和最後一個點是曲線的開頭和結尾。

一條曲線的得到過程真不容易,也就是說在計算機中曲線的得到過程並不一路順風,並不像咱們徒手畫一條曲線那麼簡單。若是你們畫過素描,應該知道一個圓應該怎麼畫。也許有人會說,圓這麼簡單,徒手就是一個大餅。對此只能說少年你太簡單了。素描的圓並非一蹴而就,而是不斷地切割,經過線段慢慢地得出一個圓。固然這只是一個比喻,計算機中的曲線是經過無數的線段組合而成的。.net

Bézier curve的實例

假設咱們將曲線分爲10段,貝塞爾曲線就是經過P0~N個點控制,從P0出發,在P0~N這些點的N-1條連線中尋找線段1/10處的點,再連接新的點得出N-2條連線,尋找新得出的線段中1/10處的點,如此循環,直至只剩兩點一線,在這條最終的線上尋找一個最終點,也就是組成曲線的點。而後查找2/10處的點,初次循環,直至到達PN。

是否是有點懵,一條曲線的誕生之路真艱辛。來!讓咱們經過實例來feel一下。咱們是如何經過定義幾個點來控制一條曲線的。

線性Bézier curves

線性Bézier curves是由兩個點P0和P1控制造成的,這個是最簡單的,就是初中(也許是小學了)學的一次函數。你們也許會質疑爲何我要解釋這麼簡單的問題,筆者你是否是傻了。(放開我,我沒瘋,我還能夠繼續。)上一節提到了曲線實際上是由無數的線段組成的,所以這個線性的Bézier curve固然就是基礎啦!

二次Bézier curves

好了離開了一次函數,咱們要進入二次函數了。二次Bézier curves是由三個點P0,P1和P2組成的。從這裏開始,咱們就要打開新世界的大門了,經過上一節簡單的線性Bézier curves咱們開始推導二次Bézier curves的做圖方式以及數學公式。

公式推導:

三次Bézier curves

終於來到了CSS中animation-timing-function:cubic-bezier(p1x,p1y,p2x,p2y)所須要的曲線了。這個曲線,咱們能夠經過上述的二次依葫蘆畫瓢畫出來,不一樣的是動態的線段又多了兩條。 公式推導:

這個分解圖畫起來,有點兇殘,因此筆者作了一個Canvas動畫

在線enjoy的地址:codepen

若是是隻是使用,咱們能夠經過一個做弊網站獲取到經常使用的時間曲線參數。

三次Bézier curves和CSS的時間函數的關係

相信你們都發現了上文提到的CSS中的animation-timing-function:cubic-bezier(x1,y1,x2,y2)這個屬性,其實就是三次貝塞爾曲線的一個應用,不過裏的第一個點和最後一個點的固定的,能夠調節的以後P1和P2。

雖然繪製貝塞爾曲線不難,只要理解了其原理,畫一個曲線相信都難不倒你們。可是CSS的時間函數真的難解,由於咱們一般是經過時間t,來得出(x,y)的座標,從而繪製曲線,可是在CSS的時間函數中,咱們使用的可不是這個方式哦。而是經過已知的x,求出y的值。這裏的難點在於,須要求解一個3元一次方程(有興趣的能夠去解三元一次方程,得出t,在帶入公式獲得y)。

也有大神作了這個網站供咱們玩轉貝塞爾曲線函數,這樣就不用本身去解三元一次方程了。

遞歸搞定全部類型貝塞爾曲線

雖然咱們能夠導出公式來計算貝塞爾曲線的每一個點的位置,可是咱們能夠用另外一種更加暴力的方式來完成,也更加直觀。

既然貝塞爾曲線是直線截出來,那麼我就能夠用遞歸一層層回調到只剩一個點,而後根據t再計算新的點,鏈接這些點個人曲線就造成啦!

每個貝塞爾曲線都是由線性遞歸而來,那麼先寫一個線性的公式。

function linearBezierCurze(a, b, c, d){
    //(a,b),(c,d)
    let s1 = c - a, s2 = d - b
    return function (t) {
        return [
            s1 * t + a,
            s2 * t + b
        ]
    }
}
複製代碼

接着對定位點們利用reduce計算兩點之間的新的點,直至新的點只剩一個。

function drawPoints(point){
  let newPoint=[]
  point.reduce((p,c)=>{
     newPoint.push(linearBezierCurze(...p,...c)(t))
     return c      
  })
  if(newPoint.length===1){
    return newPoint[0]
  }else{
    return drawPoints(newPoint)
  }
}
複製代碼

筆者寫了一個在線play的demo,你們能夠加多點玩,寫的比較簡陋,不要嫌棄:demo

總結

筆者溜了溜了……

參考文檔來自wiki百科

相關文章
相關標籤/搜索