canvas進階——貝塞爾公式推導與物體跟隨複雜曲線的軌跡運動

寫在最前

在以前的這篇文章中咱們提到了對於貝塞爾公式的運用。本次分享一下如何推導貝塞爾公式以及附一個簡單的?即小球跟隨曲線軌跡運動。html

歡迎關注個人博客,不按期更新中——git

效果預覽

2017-12-26 14_18_42

demo地址github

對於如何繪製連續的貝塞爾曲線能夠參照這篇文章:基於canvas使用貝塞爾曲線平滑擬合折線段canvas

在本例中生成的曲線由以上文章中的源碼提供。動畫

貝塞爾曲線公式推導

7460499-2603066c32c19ba9

上面這張圖是貝塞爾曲線的完整公式,看起來一臉懵逼=。=,由於這是N階的推導公式,本次咱們以一二階貝塞爾公式的推導來理解一下這個推導公式的由來。先來看下網上流傳已久的幾張貝塞爾動圖:this

1012380-20170218214830535-1198588161

1012380-20170218214945519-1357139579

1012380-20170218215045082-1043570102

在這三張圖中最重要的部分是咱們須要理解變量t。t的取值範圍是0-1。從上面的gif中也能夠看出來彷佛曲線的繪製過程就是t從0到1的過程。嗯其實就是這樣的。t的真實含義是什麼呢?spa

在p0p一、p1p二、p2p3等等的起點到控制點再到終點的連線中,每段連線都被分割成了兩部分(仔細看動圖中的黑色、綠色、藍色圓點),各段連線中兩部分的比值都是相同的,比值範圍是0到1,而這個比值就是tprototype

來看下面的一階貝塞爾曲線示意圖:code

image

pt是p0p1上的任意一點,p0pt / ptp1 = t。從而咱們能夠引出下面的推導視頻

image

此時t爲時間,v爲速度。咱們能夠看作從p0到p1的距離等於固定速度乘以固定時間

image

故到p上某一點的時間爲固定的速度乘以某個時間值。同時固定的速度已經已經能夠表示爲上面的推導公式。此時等式右邊就造成了t(0,1) / t;即至關於某個時間值 / 固定時間值,即產生了咱們一開始所強調的變量t,其取值範圍爲[0,1]。從而下面的等式也就比較好理解了。

image

至此一階貝塞爾曲線咱們已經推到了出來,其中變量爲起點、終點與比值t。

那麼二階公式如何從一階過渡過去呢?

來看下面這張圖:

image

其中Pp(t)的通過路徑就是咱們所求的二階貝塞爾曲線,那麼其實咱們也能夠將其從一階進行演變:

image

咱們先將pa、pb兩個點所連線段當作一階曲線,以後再由兩端一階曲線分別表示pa、pb,最後就獲得了咱們的二階曲線公式。仔細觀察就能發現這和咱們最初的完整公式是相同的:

7460499-2603066c32c19ba9

其中n選擇不一樣數值時就能夠得出不一樣階的曲線公式。同時從上面的推導過程也能夠知道,不管是幾階曲線,咱們均可以徹底由一階來表示,而這個「表示」的過程就是咱們在上面看到的造成動畫中那些輔助線。故能夠感覺下做者本身寫的曲線造成動畫中的效果,每段輔助線均由一階曲線造成:

2017-12-28 17_21_52

相關地址

物體跟隨複雜曲線軌跡運動

當咱們知道曲線的公式有何而來以後,如何讓小球沿着曲線運動就很好理解了。咱們生成的每段曲線都是能夠用公式表示出來的,也正因如此咱們就能夠獲得每一個t值時的曲線座標點。從而知道物體的繪製座標。

//核心邏輯
LinearGradient.prototype.drawBall = function() {
    var self = this
    var item = ctrlNodesArr[ctrlDrawIndex] 
    //存儲了各段曲線的控制點
    //各段曲線均爲三階貝塞爾,故下面計算x,y值代入到了三階公式中
    var ctrlAx = item.cAx,//各個控制點
        ctrlAy = item.cAy,
        ctrlBx = item.cBx,
        ctrlBy = item.cBy,
    ...
    if(item.t > 1) {
        ctrlDrawIndex++ //當一段曲線的t>1說明曲線已經走到頭
    }else {
        self.ctx.clearRect(0, 0, self.width, self.height)
        item.t += 0.05
        var ballX = ox * Math.pow((1 - item.t), 3) + 3 * ctrlAx * item.t * Math.pow((1 - item.t), 2) + 3 * ctrlBx * Math.pow(item.t, 2) * (1 - item.t) + x * Math.pow(item.t, 3)
        var ballY = oy * Math.pow((1 - item.t), 3) + 3 * ctrlAy * item.t * Math.pow((1 - item.t), 2) + 3 * ctrlBy * Math.pow(item.t, 2) * (1 - item.t) + y * Math.pow(item.t, 3)
        //代入三階貝塞爾曲線公式算出小球的座標值
        self.ctx.beginPath()
        self.ctx.arc(ballX, ballY, 5, 0, Math.PI * 2, false)
        self.ctx.fill()
    }
    if(ctrlDrawIndex !== ctrlNodesArr.length) {
        window.requestAnimationFrame(newMap.drawBall.bind(self))
    }
}

其餘canvas相關文章

最後

demo地址:這裏✨✨

源碼地址:歡迎star

慣例po做者的博客,不定時更新中——

有問題歡迎在issues下交流。

相關文章
相關標籤/搜索