iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 18
5

影片彈幕

昨天介紹了如何將 Video 來源同步繪製到 Canvas 上面,並且套用我們先前做的濾鏡效果,今天繼續來介紹在影片中很常見的觀眾互動彈幕效果。

在前端處理直播也是一個另外很深的領域了,雖然這邊的影片不是直播串流來,但在做彈幕上應該是相通的

首先我們可以先把昨天的影片給藏起來了,因為我們有設定了自動播放,所以其實他還是會繼續動作,在 css 中加入

 video {
    display: none;
  }

但後來發現影片有時候不會自動播放,後來查了一下資料發現可能跟 autoplay policy 有關,簡單來說自動播放的影片是不能有聲音的,必須透過一些使用者的互動如點擊等操作才能正常播放,但聲音在這邊對我們來說沒差,所以我們可以直接靜音

 <video
  src="../../../assets/video.mp4"
  autoplay
  loop
  muted
  ref="video"
  @play="drawCanvas"
  :style="videoStyle"
></video>

也可以使用 javascript 去作控制,play 會回傳一個 Promise ,我們就可以透過這個來判斷是不是有成功播放了

video.play().then(() => {
   console.log('play video start')
 }).catch((err) => {
   console.log('play video error', err)
 })

一切順利後,那我們就可以來實做一個彈幕吧!在初始化的時候需要傳入目標 Canvas,然後做一些基本的初始化

class Barrage {
  constructor({ ctx, width, height }) {
    this.ctx = ctx
    this.width = width
    this.height = height
    this.data = []
  }
}

接著新增一個函數,讓使用者可以新增需要出現的彈幕,目前做一些基本的參數設定,主要就是設定三個比較關鍵的參數

  1. x : 彈幕出現的橫軸位置,目前設定在最右邊開始
  2. y : 彈幕出現的縱軸位置,目前是隨機高度
  3. speed : 代表彈幕每次更新時移動的距離

其他參數就依據彈幕的複雜度大家自己新增

addBarrage(options) {
    const barrage = {
      x: this.width, // 初始值是畫面整個寬度,也就是在最右邊
      y: this.height * Math.random(), // 隨機在任一高度出現
      speed: options.speed || Math.random() * 10 + 0.5, // 每次移動速度
      fontSize: options.fontSize || Math.random() * 10 + 16, // 繪製字體大小
      color:
        options.color || `#${(((1 << 24) * Math.random()) | 0).toString(16)}`, // 字體顏色
      text: options.text || 'default' // 文字
    }
    this.data.push(barrage)
  }

接著提供一個繪製的方法,讓需要更新的時候可以使用,主要就是遍歷目前擁有的彈幕資料,並且將他的 x 軸為值去減掉速度,就會達成往左邊移動的效果

 draw() {
    for (let i = this.data.length - 1; i >= 0; i -= 1) {
        const barrage = this.data[i]
        this.ctx.font = `${barrage.fontSize}px Microsoft JhengHei, PMingLiU, sans-serif`
        this.ctx.fillStyle = barrage.color
        barrage.x = barrage.x - barrage.speed
        this.ctx.fillText(barrage.text, barrage.x, barrage.y)
      }
    }
  }

我們用了之前沒介紹過的方法 fillText ,這個參數會在 Canvas 上面繪製文字,第一個參數就是文字,而第二跟第三就是要出現位置的 x、y,所以我們透過一直改變 x 軸位置,來達成移動的效果,加上設定文字跟顏色來符合我們的設定。

基本的可以移動之後,接下來做一些清理,因為目前來說當彈幕移動到消失畫面後還是留在目前的資料裡面,所以我們要做一些確認讓已經離開畫面的彈幕可以被清理

 draw() {
    // 從最後開始,因為當檢查到超出範圍的彈幕時要把他移除
    for (let i = this.data.length - 1; i >= 0; i -= 1) {
      const barrage = this.data[i]
      this.ctx.font = `${barrage.fontSize}px Microsoft JhengHei, PMingLiU, sans-serif`
      // 確認彈幕是否超出邊界需要在加上字體本身寬度,因為 x 代表是整個文字的最左邊
      const textWidth = this.ctx.measureText(barrage.text).width
      const checkDisappear = barrage.x + textWidth < 0
      if (checkDisappear) {
        this.data.splice(i, 1)
      } else {
        this.ctx.fillStyle = barrage.color
        barrage.x = barrage.x - barrage.speed
        this.ctx.fillText(barrage.text, barrage.x, barrage.y)
      }
    }
  }

這邊有一個比較要注意的地方是,我們不能只單純看目前 x 的位置 < 0 就判斷為離開,因為 x 的位置代表的是最左邊的文字,也就是說我們還要加上文字本身的寬度才能代表整個文字已經離開,所以使用了 measureText 來測量文字的資訊。所以透過這些資訊我們就可以就文字真正消失的時候清理掉。

其實現在的 y 軸如果在邊界時也會有被裁切的問題唷,但一樣可以透過這個方法來做處理,這邊就不多說了

所以一切成功後我們就可以在我們的流程裡面加入彈幕了,前面一些初始化就不多說了,在更新事件中加入這一段,讓彈幕也跟著被更新

this.barrage.draw()
requestAnimationFrame(this.drawCanvas)

接著做個輸入框讓使用者可以操作吧

<el-input
v-model="barrageInput"
@keyup.enter.native="addBarrage"
placeholder="大家刷一波"
></el-input>
<el-button @click="addBarrage" class="button">
送出
</el-button>

在觸發後將這個資料更新,現在我們就只有把文字當作變數傳進去,如果要做得更細一點可以把我們上面有做成參數的字體、顏色等等加入。

addBarrage() {
    this.barrage.addBarrage({ text: this.barrageInput })
    this.barrageInput = ''
}

小結

DEMO

今天介紹了常看到的彈幕功能、以及一些 Canvas 的方法,也增加了互動功能,算是為接下來的效能調整鋪路,明天見囉~


上一篇
Day 17 - Canvas 影片播放
下一篇
Day 19 - Canvas 效能調整 - Web Worker
系列文
用 Javascript 當個影像魔術師30

1 則留言

0
CK Chuang
iT邦新手 4 級 ‧ 2019-10-03 21:20:46

7777777777777777777777

77777777777777777777777

我要留言

立即登入留言