最近由於專案需要客製化一個彈幕功能,所以嘗試使用canvas來開發組件。經過測試在一些低階機的效果也沒有明顯的卡頓,跟大家交流一下
彈幕效果功能介紹使用
npm i vue-barrage
參數配置
name | type | default | desc |
---|---|---|---|
barrageList | Array | [] | 彈幕數據 |
speed | Number | 4 | 彈幕滾動速度 |
loop | Boolean | true | 是否循環捲動 |
channels | Number | 2 | 彈幕軌道數 |
html樣式
<template> <div class=barrage-container> <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>js實作
監聽資料來源
watch: { barrageList (val) { if (val.length !== 0) { this.initData() // 資料初始化this.render() // 開始渲染} }}
資料初始化
barrageArray
是儲存彈幕資料用的,包括預設彈幕清單和新增彈幕項
/** * 資料初始化*/initData () { for (let i = 0; i < this.barrageList.length; i++) { // 此處處理只顯示40個字元let content = this.barrageList[i]. content.length > 40 ? `${this.barrageList[i].content.substring(0, 40)}...` : this.barrageList[i].content this.pushMessage(content, this.barrageList[i].color) }},/** * 增加資料* @param content * @param color */pushMessage (content, color) { let position = this.getPosition() // 決定跑道位置let x = this.barrageWidth // 初始位置let offsetWidth = 0 for (let i = 0, len = this.barrageArray.length; i < len; i++) { let item = this.barrageArray[i] if (position === item.position) { // 若同跑道,則往後排offsetWidth += Math.floor(this.ctx.measureText(item.content).width * 3 + 60) } } this.barrageArray.push({ content: content, // 彈幕內容x: x + offsetWidth, // 決定每個彈幕的初始位置originX: x + offsetWidth, // 儲存目前彈幕的位置,以便在循環的時候使用position: position, width: this.ctx.measureText(content).width * 3, // canvas繪製內容寬度 color: color || this.getColor() // 自訂顏色})},
初始化資料需要處理的就是計算目前彈幕的軌道、位置、寬度,以便在canvas
繪製的時候使用
繪製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, len = this.barrageArray .length; i < len; i++) { let barrage = this.barrageArray[i] try { barrage.x -= this.speed if (barrage.x < -barrage.width - 100) { // 此處判斷彈幕消失時機if (i === this.barrageArray.length - 1) { // 最後一條消失時的判斷邏輯if (!this.loop) { / /如果不是循環彈幕的話就取消繪製判斷是否循環,不循環執行cancelAnimationFrame cancelAnimationFrame(this.render) return } if (this.addArray.length !== 0) { // 此處判斷增加彈幕的邏輯this.barrageArray = this.barrageArray.concat(this.addArray) this.addArray = [] } for (let j = 0; j < this.barrageArray.length; j++ ) { // 給每個彈幕的x初始值this.barrageArray[j].x = this.barrageArray[j].originX } } } if (barrage.x <= 2 * document.body.clientWidth + barrage.width) { // 判斷何時開始繪製,如果不判斷的話會導致彈幕滾動卡頓// 繪製背景this.drawRoundRect(this.ctx , barrage.x - 15, barrage.position - 30, barrage.width + 30, 40, 20, `rgba(0,0,0,0.75)`) // 繪製文字this.ctx.fillStyle = `${barrage.color}` this.ctx.fillText(barrage.content, barrage.x, barrage.position) } } catch (e) { console.log(e) } }},
此處判斷繪製邏輯,包括何時取消,彈幕開始繪製判斷,彈幕消失判斷
其他函數
/** * 取得文字位置* 使用pathWayIndex來確認每一條彈幕所在的軌道* 返回距離頂部的距離* @TODO此處還可以優化,根據每條軌道的距離來判斷下一條彈幕出現位置*/ getPosition () { let range = this.channels let top = (this.pathWayIndex % range) * 50 + 40 this.pathWayIndex++ return top},/** * 取得隨機顏色*/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,radius, radius,radius, radius,radius, radius,radius, radius,radius, radius, 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()}
此處為彈幕服務函數
使用
<barrage ref=barrage class=barrage :barrage-list=barrageList :speed=speed :loop=loop :channels=channels/> import Barrage from 'vue-barrage'// 彈幕資料初始化'試數據測試數測試資料數測試資料', color: 'white'}]//新增彈幕this.$refs.barrage.add({ content: '增加一個新的彈幕增加一個新的彈幕', color: 'white'})結語
總的來說這個組件還有可優化的空間,後續我會繼續改進。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。