前端程序員必須掌握之三角函數在前端動畫中的應用

這是一篇舊文,舊博客停用,文章遷移至掘金從新發出來。css

開發過程當中常常有意無心地刻意避開數學相關的知識,你也知道解數學題很是枯燥無趣。平時寫動畫也儘可能使用 css3 來實現,timer-function 隨意選用,最多也就調一下 cubic-bezier,找到看着舒服的就行。可是怎樣讓動畫更順滑,寫出更貼近天然的動畫,說實話之前我沒怎麼考慮過。前端

每次當動效設計師提出,能不能這樣那樣的時候,我會理所固然地予以否決。因此有很長一段時間,我很是羨慕那些能用 canvas 繪製很酷炫的動畫的程序員。 css3

sad man in sine

先這樣吧,又不是不會動。程序員

今天來分享一下三角函數相關的內容,若是剛學前端的時候有人教我這些,我會很開心。canvas

三角函數

三角函數已是老生常談了(街舞圈稱之爲 Old School),它伴隨咱們從初中到大學,太多的公式定理,光是應付考試就花了很多時間。先簡單回顧一下,確保你還記得基礎知識。bash

勾股定理框架

最開始學三角函數的時候就是從背勾三股四弦五開始,勾股定理描述的是對於直角三角形,直角兩條邊的平方和等於斜邊的平方。函數

a^2 + b^2 = h^2

經常使用三角函數post

印象中教科書裏面只保留了 sin, cos, tan,其餘能夠經過變換獲得。學習

sinθ = a / h
cosθ = b / h
tanθ = a / b
複製代碼

極座標系和單位圓

在笛卡爾直角座標系中,任一點 (x, y) 均可以轉化成極座標表示 (r, θ),其中

r = Math.sqrt(x^2 + y^2)
θ = Math.atan2(y, x)
複製代碼

單位圓的定義是半徑爲單位長度的圓,圓上任意一點的橫座標就是對應角度的餘弦值,任意點的縱座標就是對應角度的正弦值。

單位圓

簡單的圖像變換 以正弦曲線爲例,對函數進行簡單的變換,獲得不同的結果。

正弦曲線變換

正弦曲線公式:y = A sin(Bx + C) + D

A 控制振幅,A 值越大,波峯和波谷越大,A 值越小,波峯和波谷越小; B 值會影響週期,B 值越大,那麼週期越短,B 值越小,週期越長。 C 值會影響圖像左右移動,C 值爲正數,圖像左移,C 值爲負數,圖像右移。 D 值控制上下移動。

這個公式很是有用,若是下文的代碼讓你不解,記得回來查看註解。

簡單得回顧一下以後,確保你還記得這些基礎知識,那麼這些曾經被得倒背如流的內容,和前端代碼結合會變成什麼樣?

常見的應用場景

圖像應用

最直觀的應用是使用三角函數來繪製 Wave 曲線

for (let x = 0; x < width; x++) {
  const y = Math.sin(x * a) * amplitude
}
複製代碼

簡單曲線

再結合三角函數偏移讓左右成爲波谷,中間成爲波峯,就能獲得曼妙的波紋。

for (let x = 0; x < width; x++) {
  const radians = x / width * Math.PI * 2
  const scale = (Math.sin(radians - Math.PI * 0.5) + 1) * 0.5
  const y = Math.sin(x * 0.02 + xSpeed) * amplitude * scale
}
複製代碼

圖像上的應用

以前掘金上很火的一篇文章,也是一樣的道理,使用兩層正弦函數繪製曲線,fill 以後加上 stagger 動畫,就能獲得很是酷炫的🌊效果。

wave

若是再結合鼠標位置 + lerp 動畫,就能實現堅果首頁同款的動畫。

smartisan

這篇文章大部分代碼均可以在個人 Codepen 主頁看到。

SlowInSlowOut

正餘弦曲線有很天然地緩入緩出的特性,而且在一個週期裏面從 -1 到 1 再回到 -1,很是適合用來模擬一些物理效果。由於真實世界裏面,汽車都是緩慢啓動,加速,減速,再緩慢減速直到速度變爲 0 的,突變會讓人很難受。左邊的擺球是線性勻速擺動,右邊是用了三角函數優化的結果。顯然左邊的效果設計師會打人。

swing-ball

只需使用 sin 或 cos 乘以最大角度,就能夠獲得在擺動最大角度之間的 SlowInSlowOut。

ctx.rotate(Math.cos(t / 180 * Math.PI) * Math.PI * 0.25)
複製代碼

角度控制

在開發過程當中,咱們經常須要跟角度打交道,好比在頭像左上角(45deg)顯示 Notification 紅點,用鼠標控制 rotation 等等。

前端 JS 裏面 Math.atan2(y, x) 能夠用來計算 (x, y) 和 x 軸正方向的夾角弧度值。

function getCurrentDegree () {
  const deltaX = mouse.x - window.innerWidth * 0.5
  const deltaY = mouse.y - window.innerHeight * 0.5
  return Math.atan2(deltaY, deltaX) * 180 / Math.PI
}
複製代碼

war-star

插一句,三角函數相關的動畫並不必定須要用 js 來寫,好比下面的 DEMO,使用 compass 依賴,一樣能夠作到靈活控制在特定角度的動畫(千萬不要手寫各個點的座標!!!後期沒辦法維護)

@import "compass";

  .checkbox:checked {
    ~ button {
      $per: 180 / 4;
      @for $i from 1 through 6 {
        &:nth-of-type(#{$i}) {
          $angle: $per * ($i - 1) * 1deg + 180deg;
          $x: cos($angle) * $d;
          $y: sin($angle) * $d;

          transform: translate($x, $y) rotate(0deg) ;
        }
      }
    }
  }
複製代碼

千萬不要手寫各個點的座標!

源碼

Case Study

常常用到的場景大概就這些吧,再以一個案例分析來複習一下。

前兩天在 Codepen 首頁看到熱門推薦,做者用存 css 動畫來實現一個行走的動畫,挺新穎的,然而仔細一看,腳步的動畫真心以爲彆扭,因而想用三角函數優化一下。

sad man

繪製頭部:

drawHead (t) {
    ctx.save()
    ctx.beginPath()
    ctx.translate(0, Math.sin(t) * 4)
    ctx.arc(80, -35, 35, 0, 2 * Math.PI)
    ctx.fill()
    ctx.closePath()
    ctx.restore()
  }
複製代碼

我會給每一個方法傳入週期參數 t, t 從 0 到 2 PI , 這樣能保證全部的週期運動時間同步。

head

身體和陰影的繪製都差很少,直接跳過看腳步動畫。

腳有兩隻,按道理應該是擡腳到落腳的動做完成時,其餘部位都完成了一個完整的週期,因此在繪製腳的時候,t 須要除以 2。而後第一隻腳和第二隻腳相差半個腳自身的週期,能夠直接將 t 替換成 t + Math.PI 就是第二腳的動畫。

drawFeet (t) {
	 t = t / 2
    ctx.translate(Math.cos(t) * -50, 0)

    // 另外一隻腳
    ctx.translate(Math.cos(t + Math.PI) * -50, 0)
  }
複製代碼

feet

腳步動畫自身週期的一半是在地面上的,能夠經過判斷一下 sin 值,小於 0 則不作 y 縱軸方向上的變化。

ctx.translate(Math.cos(t) * -50, Math.sin(t) > 0 ? Math.sin(t) * -35 : 0)
複製代碼

feet2

還沒完,爲了讓腳更加逼真,一樣在前半個週期作一下 rotate 。

if (t < Math.PI) {
      ctx.rotate(Math.sin(t) * Math.PI / 180 * -5)
    }
複製代碼

feet3

最終獲得的效果是這樣的:

sad man in sine

源碼

總結

現現在前端發展速度很是迅速,剛入門的同窗剛學完 jQuery 就被告知,You Dont Need jQuery。追新的腳本根本停不下來,在學習新框架新技能的同時,也別忘了基礎知識的重要性。

好了,今天就分享到這裏,但願一次聚集這麼多效果,能讓你下次使用三角函數更駕輕就熟。

以上大部分代碼均可以在個人 Codepen 主頁看到。

相關連接

這是一篇舊文,博客停用,文章遷移至掘金從新發出來。

相關文章
相關標籤/搜索