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

老姚發表於2020-01-03

先看效果:

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

你有沒有想到王者榮耀?

當然,這個效果不是我首創的,改編於 PEP 官方的 demo

如果在手機上檢視它那個的話,可以用左手控制飛船飛行,右手點選頁面發射子彈。它已經有了遊戲的雛形。(另外插一句, pointerEvent 事件非常好用的,推薦)

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

1. 總體原理

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

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

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

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

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

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

關鍵程式碼是:

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
})
複製程式碼

效果如下:

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

點選檢視效果

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 表示火焰變短還是變長。同時,根據速度的大小決定火焰的長度,這樣符合直觀感覺。

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

點選檢視效果

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

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

因此有:

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動畫技術。

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

(圖中二維碼是我的唯一微訊號,如有掘友想加的,麻煩備註下【掘金】哈。)

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

因為篇幅問題,根據以往的經驗,贊數不會太多,畢竟大家都喜歡給那種短時間看不完的文章點贊。嗯,我好像也是這樣。^_^

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

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

1.《HTML5 Canvas核心技術》

2.《HTML5+JavaScript動畫基礎》

3.《WebGL程式設計指南》

本系列一些文章可能會參考裡面的知識體系,對於一些屬於領域共識知識,如有區域性雷同,只能說:“自己憑本事學來的,怎能算抄襲。。。”。

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

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

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

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

相關文章