昨天介紹了如何將 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 = []
}
}
接著新增一個函數,讓使用者可以新增需要出現的彈幕,目前做一些基本的參數設定,主要就是設定三個比較關鍵的參數
其他參數就依據彈幕的複雜度大家自己新增
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 = ''
}
今天介紹了常看到的彈幕功能、以及一些 Canvas
的方法,也增加了互動功能,算是為接下來的效能調整鋪路,明天見囉~