先來看動畫效果:
這個動畫看起來有點難,但其實要比想象中的簡單。
它只是平移和旋轉的簡單合成,下面大體分析一下。
1. 滑鼠位置
要跟隨滑鼠移動,首先就要獲取滑鼠的位置。
可以給畫布繫結mousemove事件,這樣拿到滑鼠相對於頁面的絕對位置(pageX,pageY),再減去畫布自身的偏移量,就可以得到滑鼠相對畫布的位置。核心程式碼是:
canvas.addEventListener('mousemove', event => {
mouse.x = event.pageX - canvas.offsetLeft
mouse.y = event.pageY - canvas.offsetTop
})
複製程式碼
2. 滑鼠相對於箭頭的方向
知道滑鼠和箭頭的位置資訊,就可以算出一些有用的值,比如二者水平和垂直距離。
根據三角函式tan的反函式就可以求出相應角度。Math物件中有兩個反正切函式atan和atan2。其中比較好用的卻是不太顯眼的atan2。
var dx = mouse.x - arrow.x
var dy = mouse.y - arrow.y
var angle = Math.atan2(dy, dx)
複製程式碼
3. 旋轉動畫
使用requestAnimationFrame(canvas動畫常規操作),實時更新箭頭角度,就可以做出跟隨滑鼠旋轉的動畫啦。
;(function drawFrame() {
window.requestAnimationFrame(drawFrame)
context.clearRect(0, 0, canvas.width, canvas.height)
var dx = mouse.x - arrow.x
var dy = mouse.y - arrow.y
var angle = Math.atan2(dy, dx)
arrow.angle = angle
arrow.draw(context)
})();
複製程式碼
drawFrame作為瀏覽器下次重繪前的回撥函式,每一幀都清空畫布,然後畫出特定角度的箭頭。效果如下:
4. 平移動畫
要實現平移動畫,只需要勻速地修改箭頭地位置。
arrow.x += vx
arrow.y += vy
複製程式碼
這裡的難點在於如何計算水平速度和垂直速度。我們假設滑鼠移動速度是v,那麼三者的關係應該滿足於:
因此,角度剛才已經算出來了,那麼根據三角函式有:
vx = Math.cos(angle) * v
vy = Math.sin(angle) * v
複製程式碼
不考慮旋轉的跟隨移動效果如下:
5. 最終效果
一邊朝著滑鼠移動,一邊指向滑鼠,就能實現文章開頭的效果。
注意程式碼里加了下面的判斷,這是為了防止出現平移動畫的震盪bug。
if (dx * dx + dy * dy < v * v) {
vx = 0
vy = 0
} else {
vx = Math.cos(angle) * v
vy = Math.sin(angle) * v
}
複製程式碼
可以分析下出現震盪的原因。比如箭頭距離滑鼠1畫素,下一幀加上速度2後,距離變成了-1,朝向變成了反方向。再下一幀,距離又變成了1,然後震盪不停。這裡的解決辦法比較生硬,一旦距離小於v(速度就是一幀內移動的距離)時,直接不再移動了。
這個動畫很常見,參考書目2裡面也提到了,但沒想到原理卻出乎意料的簡單。
另外飛機遊戲中,敵機自動向你移動也是同樣的邏輯。
本文只是大體分析了一下原理,希望有所幫助。
完。
下面的內容是關於本系列的介紹。
2019年末,本人立了個flag,2020年要研究透canvas動畫技術。
(圖中二維碼是我的唯一微訊號,如有掘友想加的,麻煩備註下【掘金】哈。)
在這個系列,我想寫一些常見動畫知識,本文是第2篇,篇幅可能會長短不一。更多的請檢視我的個人主頁,或者《系列目錄》。
因為篇幅問題,根據以往的經驗,贊數不會太多,畢竟大家都喜歡給那種短時間看不完的文章點贊。嗯,我好像也是這樣。^_^
其實寫文章,主要還是給自己看的,算是自我進步的一個見證吧。抱著這種心態也許能好些。
另外關於canvas技術,我目前完整看完了3本書。算是過了基礎一關。
本系列一些文章可能會參考裡面的知識體系,對於一些屬於領域共識知識,如有區域性雷同,只能說:“自己憑本事學來的,怎能算抄襲。。。”。
開玩笑了,想法來源能提一句還是要提一句的。特別喜歡《精英日課》文章裡的一段話:
至於文章內容,canvas的API,本系列可能不會準備逐條介紹了,還請初學的童鞋見諒哈。MDN都有的,挺詳細的。同時,文章中遇到的還是會簡單提下。主要核心是闡述一些技巧和原理層面的知識個人理解吧。另外也打算分析一些codepen上炫酷動畫的實現原理,如果有時間可能會分析幾個動畫引擎,當然都是2D的。
再次感謝你閱讀到這裡。下一篇文章見。