更新:支持頭像和背景色設置css
更新:支持實時彈幕插入,支持彈幕的流式佈局html
最近因爲項目須要定製化一個彈幕功能,因此嘗試使用canvas來開發組件。通過測試在一些低端機的效果也沒有明顯的卡頓,和你們交流一下vue
npm i vue-barrage
git
name | type | default | desc |
---|---|---|---|
barrageList | Array | [] | 彈幕數據 |
speed | Number | 4 | 彈幕滾動速度 |
loop | Boolean | true | 是否循環滾動 |
channels | Number | 2 | 彈幕軌道數 |
borderColor | String | '#000' | 彈幕邊框 |
background | String | '#FFF' | 彈幕背景色 |
<template>
<div class="barrage-container">
<canvas
ref="canvasContainer"
:width="barrageWidth"
:height="barrageHeight"
style="display: none;"></canvas>
<div
class="container"
:style="{height: barrageHeight/2+'px'}"
>
<canvas
id="canvas"
ref="canvas"
:width="barrageWidth"
:height="barrageHeight"
:style="{'width': barrageWidth/2 + 'px',
'height': barrageHeight/2 + 'px'}"
/>
</div>
</div>
</template>
複製代碼
watch: {
barrageList (val) {
if (val.length !== 0) {
this.barrageQueue = val // 將數據保存在更新隊列
this.initData() // 數據初始化
window.requestAnimationFrame(this.render) // 開始渲染
}
}
}
複製代碼
barrageQueue
是須要初始化的數組(包括原始數據或者原始數據加新增數據)
waitArray
是原始數據加增長的彈幕數據
barrageArray
存儲未出現的彈幕數據,改數組數據不斷減小github
/**
* 數據初始化
*/
initData () {
for (let i = 0; i < this.barrageQueue.length; i++) { // 此到處理只顯示55個字符
let content = this.barrageQueue[i].content.length > 55 ? `${this.barrageQueue[i].content.substring(0, 55)}...` : this.barrageQueue[i].content
this.barrageArray.push({
content: content, // 初始化內容
x: this.barrageWidth, // 初始彈幕出現位置
width: this.ctx1.measureText(content).width * 3, // 文字寬度
color: this.barrageQueue[i].color || this.getColor()
})
}
this.initChannel()
},
/**
* 初始化軌道數據
* 爲每一條軌道初始化一條數據
*/
initChannel () {
for (let i = 0; i < this.channels; i++) {
let item = this.barrageArray.shift()
this.waitArray.push(item)
if (item) {
this.channelsArray[i] = [item]
} else {
this.channelsArray[i] = []
}
}
}
複製代碼
初始化數據須要處理的就是計算當前彈幕的軌道、位置、寬度,以便在
canvas
繪製的時候使用npm
canvas
/**
* 渲染
*/
render () {
this.ctx.clearRect(0, 0, this.barrageWidth, this.barrageHeight)
this.ctx.font = '30px Microsoft YaHei'
this.draw()
window.requestAnimationFrame(this.render) // 每隔16.6毫秒渲染一次,若是使用setInterval的話在低端機型會有點卡頓
}
/**
* 開始繪製 文字和背景
*/
draw () {
for (let i = 0; i < this.channelsArray.length; i++) {
for (let j = 0; j < this.channelsArray[i].length; j++) {
try {
let barrage = this.channelsArray[i][j]
barrage.x -= this.speed
if (barrage.x <= this.barrageWidth) {
this.drawRoundRect(this.ctx, barrage.x - 15, i * 46 + 8, barrage.width + 30, 40, 20, `rgba(0,0,0,0.75)`)
this.ctx.fillStyle = `${barrage.color}`
this.ctx.fillText(barrage.content, barrage.x, i * 46 + 39)
}
if (barrage.x < -(barrage.width + this.barrageWidth)) { // 彈幕超過必定距離就刪除
let item = this.channelsArray[i].shift()
item.x = this.barrageWidth
if (this.loop) { // 彈幕循環處理
let arr = this.channelsArray.reduce((a, b) => a.concat(b))
if (arr.length === 0) {
this.barrageQueue = []
this.barrageQueue = this.waitArray
this.waitArray = []
this.initData()
}
}
}
// 插入彈幕的時機
if (barrage.x <= (this.barrageWidth - barrage.width - 50) && barrage.x >= (this.barrageWidth - barrage.width - 50 - this.speed) && (j === this.channelsArray[i].length - 1) && this.barrageArray.length !== 0) {
let item = this.barrageArray.shift()
this.channelsArray[i].push(item)
this.waitArray.push(item)
}
} catch (e) {
console.log(e)
}
}
}
}
複製代碼
此處判斷繪製邏輯,包括何時取消,彈幕開始繪製判斷,彈幕消失判斷canvas
/**
* 重置數據
*/
add (obj) {
let item = {
content: obj.content,
x: this.barrageWidth,
width: this.ctx1.measureText(obj.content).width * 3,
color: obj.color || this.getColor()
}
this.barrageArray.unshift(item)
},
複製代碼
新增彈幕時將在未出現的彈幕數組首部添加一條數據數組
<style lang="scss" scoped>
.barrage-container { // 點擊事件穿透
pointer-events: none;
}
.container {
width: 100%;
overflow: hidden;
}
</style>
複製代碼
/**
* 獲取隨機顏色
*/
getColor () {
return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6);
},
/**
* 繪畫圓角矩形
* @param context
* @param x
* @param y
* @param width
* @param height
* @param radius
* @param color
*/
drawRoundRect (context, x, y, width, height, radius, color) {
context.beginPath()
context.fillStyle = color
context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2)
context.lineTo(width - radius + x, y)
context.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2)
context.lineTo(width + x, height + y - radius)
context.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2)
context.lineTo(radius + x, height + y)
context.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI)
context.fill()
context.closePath()
}
複製代碼
此處爲彈幕服務函數bash
<barrage
ref="barrage"
class="barrage"
:barrage-list="barrageList"
:speed="speed"
:loop="loop"
:channels="channels"/>
import Barrage from 'vue-barrage'
// 彈幕數據格式
this.barrageList = [{
content: '試數據測試數測試數據數測試數據',
color: 'white'
}]
// 新增彈幕方式調用
this.$refs.barrage.add({
content: '增長一條新的彈幕增長一條新的彈幕', color: 'white'
})
複製代碼
這一次改版作了插入彈幕當即顯示,原來的彈幕插入只會出如今列表的最後。彈幕的插入軌道也是上一條彈幕出現必定距離後自動插入到後面,符合流式佈局的格式。dom
若是這篇文章對你有幫助,不妨點個贊吧!