【canvas】搖桿操控飛船的動畫原理

先看效果:javascript

你有沒有想到王者榮耀?java

固然,這個效果不是我獨創的,改編於 PEP 官方的 demojquery

若是在手機上查看它那個的話,能夠用左手控制飛船飛行,右手點擊頁面發射子彈。它已經有了遊戲的雛形。(另外插一句, pointerEvent 事件很是好用的,推薦)git

本文將用我的思路,從頭實現這個搖桿控制飛船效果,代碼要比官方的清晰、好懂。github

1. 整體原理

簡單分析一下這個效果的原理。編程

頁面上總共有兩個組件:飛船和搖桿。canvas

搖桿用來控制飛船的移動。它又分爲兩部分,杆座(底座)和杆頭。鼠標按下初始化底座的位置,鼠標移動時,移動杆頭的位置。微信

杆頭與底座的相對位置,爲飛船的運動提供了速度大小和方向。動畫

計算出距離以及夾角,並選取距離的十分之一,做爲飛船的速度大小。ui

關鍵代碼是:

var dx = control.xHead - control.xTail
var dy = control.yHead - control.yTail
var d = Math.sqrt(dx * dx + dy * dy)
ship.v = d * 0.1
ship.angle = Math.atan2(dy, dx)
複製代碼

核心原理就這些。

本質上,效果核心是「控制」,所以最關鍵的就是要弄清楚控制器與被控制物兩者變化對應關係。一旦掌握這個,剩下的就是敲敲代碼啦。(說得輕巧。。)

2. 搖桿的實現

搖桿分爲杆頭和底座。底座是兩個圓,杆頭是一個圓,兩者都有本身的座標。

class Control{
  constructor() {
    this.xTail = 0
    this.yTail = 0
    this.xHead = 0
    this.yHead = 0
    this.visible  = false
  }
  draw(context) {
    if (!this.visible) return
    context.save()
    context.beginPath() 
    context.strokeStyle = "cyan"
    context.lineWidth = 6
    context.arc(this.xTail, this.yTail, 40, 0, Math.PI * 2) 
    context.stroke()
    context.beginPath()
    context.lineWidth = 2
    context.arc(this.xTail, this.yTail, 60, 0, Math.PI * 2)  
    context.stroke()
    context.beginPath()
    context.arc(this.xHead, this.yHead, 40, 0, Math.PI * 2) 
    context.stroke()
    context.restore()
  }
}
複製代碼

visible 屬性用來控制搖桿是否可見,底座的兩個圓的半徑是40和60,杆頭的圓也爲40。

與鼠標的交互操做,相對比較簡單了。與拖拽效果的實現相似:

  1. 鼠標按下,搖桿可見,更新底座和杆頭的位置。
  2. 鼠標移動,更新杆頭的位置
  3. 鼠標擡起,搖桿不能夠見。
var control = new Control()
var mouse = captureMouse(canvas) // 獲取鼠標的實時位置,具體參見效果的完整代碼
canvas.addEventListener('mousedown', function() {
  control.xHead = control.xTail = mouse.x
  control.yHead = control.yTail = mouse.y
  control.visible = true
})
canvas.addEventListener('mousemove', function() {
  if (control.visible) {
    control.xHead = mouse.x
    control.yHead = mouse.y
  }
})
canvas.addEventListener('mouseup', function() {
  control.visible = false
})
複製代碼

效果以下:

點擊查看效果

3. 飛船的實現

飛船自己由兩個三角形組成,一個用於表示機身,一個用於表示噴出的火焰。並有速度和角度屬性,以便控制器操控。下面是部分代碼:

class Ship{
  constructor() {
    this.x = 0
    this.y = 0
    this.v = 0
    this.angle = 0
    this.flag = false
  }
  draw(context) {
    context.save()
    context.translate(this.x, this.y)
    context.rotate(this.angle)
    context.beginPath()
    context.moveTo(-15, -10)
    context.lineTo(-15, 10)
    context.lineTo(10, 0)
    context.closePath()
    context.lineWidth = 2
    context.strokeStyle = "white"
    context.stroke()
    if (this.v > 0) {
      context.beginPath()
      context.moveTo(-15, -5)
      context.lineTo(-15 - this.v * (this.flag ? 1 : 3) , 0)
      context.lineTo(-15, 5)
      context.closePath()
      context.stroke()
      this.flag = !this.flag
    }
    context.restore()
  }
}
複製代碼

這裏使用了 translate 來實現移動,使用了 rotate 來實現旋轉,當有速度時,開始噴出火焰。爲了實現火焰閃爍效果,這裏使用 flag 表示火焰變短仍是變長。同時,根據速度的大小決定火焰的長度,這樣符合直觀感受。

點擊查看效果

飛船要動起來,須要根據速度和夾角更新 this.x 和 this.y,也就是要求出相應的水平速度和垂直速度。三個速度知足以下三角關係:

所以有:

var vx = this.v * Math.cos(this.angle)
var vy = this.v * Math.sin(this.angle)
this.x += vx
this.y += vy
複製代碼

把上述代碼添加到 draw 方法裏後,飛船就飛起來了。

4. 最終效果

有了兩個組件後,接下來要讓兩者配合起來。根據前面原理的說明,移動搖桿時,須要更新飛船的速度和角度。

canvas.addEventListener('mousemove', function() {
  if (control.visible) {
    control.xHead = mouse.x
    control.yHead = mouse.y
    var dx = control.xHead - control.xTail
    var dy = control.yHead - control.yTail
    var d = Math.sqrt(dx * dx + dy * dy)
    ship.v = d * 0.1
    ship.angle = Math.atan2(dy, dx)
  }
})
複製代碼

點擊查看最終效果

代碼裏還有兩點沒有提到,一個是飛船飛出邊界和最大速度的限制問題,兩者比較簡單,所以這裏省略了。

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

本文完。



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

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

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

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

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

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

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

1.《HTML5 Canvas核心技術》

2.《HTML5+JavaScript動畫基礎》

3.《WebGL編程指南》

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

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

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

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

相關文章
相關標籤/搜索