【canvas】動畫原理の胡克定律

先看下平面拖拽效果javascript

還有水平拖放版本java

先說明一下,這個彈簧效果受啓發於第二本參考書。spring

不知道你看到後有沒有以爲很複雜,爲了完成它,我大約用了一個小時左右。編程

本文會詳細介紹一下實現的具體原理,研究明白後,動畫的運動學基礎應該也算是過關了吧。canvas

1. 運動學通常原理

首先從運動動畫的基本原理提及。瀏覽器

咱們知道,根據 requestAnimationFrame 實時更新小球 ball 的座標就能實現動畫。微信

ball.x += 3
ball.y += 4
複製代碼

每一幀,小球向右移動 3px,向下移動 4px。其中 3 和 4 就是速度的刻畫。所以能夠把它們分別定義成水平速度和垂直速度。動畫

vx = 3
vy = 4
ball.x += vx
ball.y += vy
複製代碼

上面代碼中速度是常量,於是運動是勻速直線運動。若是要加速運動,那麼就須要引入加速度:ui

ax = 0.5
ay = 1
vx += ax
vy += ay
ball.x += vx
ball.y += vy
複製代碼

根據牛二定律,力與加速度成正比。this

f = m * a

咱們假設小球的質量是單位 1,那麼就能夠把力的模型引入進來。好比小球的拋射運動很容易實現:

vx = 2.5
vy = -6
gravity = 0.1
vy += gravity
ball.x += vx
ball.y += vy
複製代碼

效果以下:

其中小球類的代碼還是常規的實現:

class Ball{
  constructor() {
    this.x = 0
    this.y = 0
    this.radius = 20
  }
  draw(context) {
    context.save()
    context.translate(this.x, this.y)
    context.fillStyle = 'red'
    context.beginPath()
    context.arc(0, 0, this.radius, 0, Math.PI * 2)
    context.fill()
    context.restore()
  }
}
複製代碼

說到力,爲了模擬真實世界物體的運動,有時咱們還要考慮摩擦力。這裏咱們假設運動物體的速度每一幀後都會衰減。這裏使用模型:

friction = 0.99
vx *= friction
vy *= friction
ball.x += vx
ball.y += vy
複製代碼

每一幀速度都打會 0.99 折。0.99 看起來接近 1,可是它是指數衰減的。瀏覽器通常幀率 60 左右,所以 0.99 的 60 次方降很快降到一個接近 0 的數。

效果以下:

2. 胡克定律

有了通常運動學原理的鋪墊後,接下來正式分析開篇動畫的原理。

其中小球除了受重力和摩擦力的影響外,還受到了彈簧的力。

高中咱們都學過胡克定律:彈簧自身受到的力與彈簧的形變量成正比。

F = k * x

其中 k 是彈簧係數,由彈簧自身決定。x 是彈簧的形變量。

跟據做用力與副作用力相等,小球受到的力爲:

f = -k * x

所以水平簡諧運動的關鍵代碼:

var x = ball.x -spring.x - initLength
var f = -k * x
vx += f
ball.x += vx
spring.length = ball.x - spring.x
複製代碼

其中 x 是彈簧形變量。f 是小球受到的彈簧力,最後兩句代碼是更新小球的速度和彈簧的當前長度。

這裏沒有加入摩擦力,所以它會無限運動下去。效果以下:

爲了讓小球能運動起來,初始時,把小球拉開了一段距離。這個距離也是彈簧的振幅。

var amplitude = 70
ball.x += amplitude
var vx = 0
複製代碼

其中彈簧主要使用二次貝塞爾曲線 quadraticCurveTo 模擬的。

class Spring{
  constructor(length) {
    this.x = 0
    this.y = 0
    this.angle = 0
    this.length = length
  }
  draw(context) {
    context.save()
    context.translate(this.x, this.y)
    context.rotate(this.angle)
    context.strokeStyle = 'white'
    context.beginPath()
    context.moveTo(0, 0)
    var n = 16
    var h = 25
    var d = (this.length - 2 * h) / n
    context.lineTo(h, 0)
    for (var i = 0; i < n; i++) {
      var dir = i % 2 == 0 ? 1 : -1
      context.quadraticCurveTo(h + d * ( i + 0.5), 20 * dir,  h + d * (i + 1), 0)
    }
    context.lineTo(this.length, 0)
    context.stroke()
    context.restore()
  }
}
複製代碼

下面分析一下開篇效果實現須要注意的細節。

3. 力的合成與分解

在上面的基礎上,加入摩擦力和拖拽的邏輯,很容易實現開篇的第二個效果。這裏再也不過多解釋了。

而第一個效果稍微複雜一點。首先每一幀都要更新彈簧頭的位置。

spring.x = mouse.x
spring.y = mouse.y
複製代碼

此時小球收到的彈簧力是:

var dx = ball.x - spring.x
var dy = ball.y - spring.y
var x = Math.sqrt(dx * dx + dy * dy) - initLength
var f = -k * x
複製代碼

爲了求出水平和垂直的加速度,此時就須要力的分解了:

根據三角關係有:

var angle = Math.atan2(dy, dx)
var fx = f * Math.cos(angle)
var fy = f * Math.sin(angle)
複製代碼

最後是彈簧力、重力和摩擦力合成邏輯:

vx += fx
vy += fy
vy += gravity
vx *= friction
vy *= friction
ball.x += vx
ball.y += vy
複製代碼

另外注意一點是,在更新小球后位置後,由於小球位置變了,所以還須要計算彈簧的此時的新長度和方向。

點擊查看最終效果和完整代碼

感謝你看到這裏,但願有所幫助。

本文完。



下面的內容是關於本系列的介紹。

2019年底,本人立了個flag,2020年要研究透canvas動畫技術。

(圖中二維碼是個人惟一微信號,若有掘友想加的,麻煩備註下【掘金】哈。)

在這個系列,我想寫一些常見動畫知識,本文是第4篇,篇幅可能會長短不一。更多的請查看個人我的主頁,或者《系列目錄》

由於篇幅問題,根據以往的經驗,贊數不會太多,畢竟你們都喜歡給那種短期看不完的文章點贊。嗯,我好像也是這樣。^_^

其實寫文章,主要仍是給本身看的,算是自我進步的一個見證吧。抱着這種心態也許能好些。

另外關於canvas技術,我目前完整看完了3本書。算是過了基礎一關。

1.《HTML5 Canvas核心技術》

2.《HTML5+JavaScript動畫基礎》

3.《WebGL編程指南》

本系列一些文章可能會參考裏面的知識體系,對於一些屬於領域共識知識,若有局部雷同,只能說:「本身憑本事學來的,怎能算抄襲。。。」。

開玩笑了,想法來源能提一句仍是要提一句的。特別喜歡《精英日課》文章裏的一段話:

至於文章內容,canvas的API,本系列可能不會準備逐條介紹了,還請初學的童鞋見諒哈。MDN都有的,挺詳細的。同時,文章中遇到的仍是會簡單提下。主要核心是闡述一些技巧和原理層面的知識我的理解吧。另外也打算分析一些codepen上炫酷動畫的實現原理,若是有時間可能會分析幾個動畫引擎,固然都是2D的。

再次感謝你閱讀到這裏。下一篇文章見。

相關文章
相關標籤/搜索