基于Vue設(shè)計實現(xiàn)一個彈幕組件
1.關(guān)于彈幕設(shè)計思想
1.1業(yè)務(wù)層 | 視圖層(全局組件)
從業(yè)務(wù)角度來說,如果你設(shè)計的是全局彈幕組件,你要考慮以下幾點。
- 容器的高度?
- 容器層次結(jié)構(gòu)劃分?
- 渲染彈幕的方式,使用組件的人應(yīng)該傳遞什么數(shù)據(jù)?
- 是否支持全屏彈幕?
- 是否支持彈幕關(guān)閉和開啟?
- 是否需要重置彈幕?
- 是否支持暫停彈幕?
- 是否需要集成發(fā)送功能?
設(shè)計方案考慮完整了以后,你將可以開始考慮 數(shù)據(jù)層的設(shè)計
1.2數(shù)據(jù)層
從數(shù)據(jù)角度來說每一條彈幕無非是一個element,然后把彈幕內(nèi)容放到這個element元素中,并且給 element 添加動畫,那接下來,你應(yīng)該這樣考慮。
- 彈幕是JS對象?它的屬性有哪些?
- 誰去管理這些彈幕?如何讓他能夠支持暫停和關(guān)閉?
- 你如何把后臺的數(shù)據(jù),與你前臺的一些靜態(tài)數(shù)據(jù)進(jìn)行合并,創(chuàng)造出一個完整對象?
- 你怎么去渲染這些彈幕?
- 你想要幾秒創(chuàng)建一次彈幕并在容器內(nèi)顯示和運行?
- 彈幕具備哪些靈活的屬性?運行動畫時間 , 用戶自己發(fā)布的彈幕樣式定制? 又或者,彈幕需要幾條彈道內(nèi)運行等等這些你都需要考慮。
數(shù)據(jù)設(shè)計方案考慮完整了以后,你將可以開始考慮 數(shù)據(jù)管理層的設(shè)計
1.3數(shù)據(jù)管理層
從管理的角度來說,外界調(diào)用某些方法,你即可快速的響應(yīng)操作,例如外界調(diào)用 open 方法,你就播放彈幕,調(diào)用Stop方法,你就關(guān)閉彈幕 接下來,你應(yīng)該考慮以下幾點。
- 面向?qū)ο笤O(shè)計,應(yīng)該提供哪些方法,具備哪些功能?
- 調(diào)用了指定的方法,應(yīng)該怎么對數(shù)據(jù)進(jìn)行操作。
- 如何對彈幕做性能優(yōu)化?
到這里 , 我們設(shè)計方案基本完成,接下來我們可以開始編寫代碼。
2.代碼實現(xiàn)
2.1數(shù)據(jù)層設(shè)計方案實現(xiàn)
我們需要構(gòu)建一個 Barrage 類 ,我們每次去創(chuàng)建一個彈幕的時候都會 new Barrage,讓他幫助我們生成一些彈幕屬性。
export class Barrage {
constructor(obj) {
// 每次 new Barrage() 傳入一個 后臺返回的數(shù)據(jù)對象 obj
const { barrageId, speed, level, top, jumpUrl, barrageContent, animationPlayState, ...args } = obj
this.barrageId = barrageId; // id : 每條彈幕的唯一id
this.speed = speed; // speed : 彈幕運行的速度,由外界控制
this.level = level; // level : 彈幕的層級 --> 彈幕可分為設(shè)計可分為 上下 1 , 1 兩個層級 ,可決定彈幕的顯示是全屏還是半屏顯示
this.top = top; // top :彈幕生成的位置相對于 level 的層級 決定 ,相對于 Level 層級 盒子距離頂部的位置
this.jumpUrl = jumpUrl; // jumpUrl :點擊彈幕需要跳轉(zhuǎn)的鏈接
this.barrageContent = barrageContent; // barrageContent : 彈幕的內(nèi)容
this.animationPlayState = ''; // 設(shè)計彈幕 是否可 點擊暫停功能
this.color = '#FFF' // 彈幕顏色
this.args = args // 除去Barrage類之外的一些數(shù)據(jù)屬性全部丟到這里,例如后臺返回的數(shù)據(jù)
}
}2.2數(shù)據(jù)管理層設(shè)計方案實現(xiàn)
我們在這里實現(xiàn)了 , 彈幕的 增加,刪除,初始化,重置,關(guān)閉 , 開啟功能
1.實現(xiàn)彈幕開啟功能
BarrageManager.js
export class BarrageManager {
constructor(barrageVue) {
this.barrages = []; // 填彈幕的數(shù)組
this.barragesIds = [] // 批量刪除彈幕的數(shù)組id
this.sourceBarrages = [] // 源彈幕數(shù)據(jù)
this.timer = null //控制彈幕的開啟和關(guān)
this.barrageVue = barrageVue // 彈幕組件實例
this.deleteCount = 0, // 銷毀彈幕的總數(shù)
this.lastDeleteCount = 0, // 最后可銷毀的數(shù)量
this.row = 0,
this.count = 0
}
init(barrages) {
this.sourceBarrages = barrages
this.deleteCount = parseInt(this.sourceBarrages.length / deleteQuantity.FIFTY) // 計算可刪除數(shù)量
this.lastDeleteCount = this.sourceBarrages.length % deleteQuantity.FIFTY // 計算 最后一次可刪除數(shù)量
}
/**
*
* @param {*} barrages 接收一個彈幕數(shù)組數(shù)據(jù)
* @description 循環(huán)創(chuàng)建 彈幕對象 ,將后臺數(shù)據(jù)與 創(chuàng)建彈幕的屬性結(jié)合 存入彈幕數(shù)組
*/
loopCreateBarrage(barrages) {
const { rows, createTime, crearteBarrageObject } = this.barrageVue
let maxRows = rows / 2 // 最大的彈幕行數(shù)
this.timer = setInterval(() => {
for (let i = 0; i < 1; i++) {
let barrageItem = barrages[this.count]
if (this.row >= maxRows) { this.row = 0 } // 如果當(dāng)前已經(jīng)到了 最大的彈幕行數(shù)臨界點則 回到第0 行彈道繼續(xù) 創(chuàng)建
if (!barrageItem) return clearInterval(this.timer) // 如果取不到了則證明沒數(shù)據(jù)了 , 結(jié)束彈幕展示
const item = crearteBarrageObject({ row: this.row, ...barrageItem }) // 添加對象到 彈幕數(shù)組中
this.addBarrage(item)
this.count++ // 用于取值 ,取了多少條
this.row++ // 用于彈道
}
}, createTime * 1000);
}
/**
* @param {*} barrages 傳入一個彈幕數(shù)組數(shù)據(jù)
* @returns 無返回值
* @description 調(diào)用 該方法 開始播放彈幕
*/
open(barrages) {
if (barrages.length === 0) return
this.init(barrages)
this.loopCreateBarrage(this.sourceBarrages)
}
}在這里我們初始化了一個 open 方法,并接收一個數(shù)組 ,并調(diào)用了 init 方法 去做初始化操作,并調(diào)用了 循環(huán)創(chuàng)建的方法,沒 createTime 秒創(chuàng)建一條彈幕,加入到彈幕數(shù)組中。
2.連接視圖層
2.3視圖層 | 業(yè)務(wù)層設(shè)計方案實現(xiàn)
index.vue
<template>
<div class="barrage">
<div class="barrage-container" ref="barrageContainer">
<div class="barrage-half-screen" ref="halfScreenContainer">
<template v-for="item in barrageFiltering.level1">
<barrage-item
:item="item" :class="{pausedAnimation : paused }"
:options='barrageTypeCallback(item)'
@destory="destoryBraageItem" :key="item.barrageId">
</barrage-item>
</template>
</div>
<div class="barrage-full-screen" v-if="fullScreen">
<template v-for="item in barrageFiltering.level2">
<barrage-item
:item="item" :class="{pausedAnimation : paused }"
:options='barrageTypeCallback(item)'
@destory="destoryBraageItem" :key="item.barrageId">
</barrage-item>
</template>
</div>
</div>
<user-input ref="publishBarrage" v-if="openPublishBarrage" @onBlur="handleBlur">
<template #user-operatio-right>
<!-- 處理兼容性問題 ios 和 安卓 觸發(fā)點擊事件 -->
<div class="send" @click="sendBarrage($event)" v-if="IOS">
<slot name="rightRegion"></slot>
</div>
<div class="send" @mousedown="sendBarrage($event)" v-else>
<slot name="rightRegion"></slot>
</div>
</template>
</user-input>
</div>
</template>
export default {
created () {
this.barrageManager = new BarrageManager(this)
},
mounted() {
// 初始化彈幕渲染數(shù)據(jù)
this.initBarrageRenderData();
},
data() {
return {
barrageManager : null,
isClickSend: false,
paused : false
};
},
methods : {
initBarrageRenderData() {
this.barrageManager.open(this.barrages);
},
},
computed : {
barrageFiltering() {
return {
level1:
this.barrageManager.barrages.filter(
item => item.level === barrageLevel.LEVEL1
) || [],
level2:
this.barrageManager.barrages.filter(
item => item.level === barrageLevel.LEVEL2
) || []
};
},
}
}視圖層知識點回顧
在這里我們在彈幕組件創(chuàng)建的時候去創(chuàng)建了一個 彈幕管理對象,并且在掛載的時候去初始化了以下 彈幕渲染的數(shù)據(jù),于是我們調(diào)用了 彈幕管理類的 open方法,這樣當(dāng)組件掛載時,就會去渲染 barrageFiltering 數(shù)據(jù),這里我們是在管理類中拿到了管理類中循環(huán)創(chuàng)建的數(shù)據(jù)。
open 方法實現(xiàn)
到這里我們的彈幕的開啟基本上已經(jīng)完成了,可以看得出,如果你是這樣設(shè)計的,你只需要在組件中調(diào)用管理類的一些方法,它就能幫你完成一些功能。
3.實現(xiàn)彈幕關(guān)閉功能
barrageManager.js
class BarrageManager {
constructor(barrageVue) {
this.barrages = []; // 填彈幕的數(shù)組
this.barragesIds = [] // 批量刪除彈幕的數(shù)組id
this.sourceBarrages = [] // 源彈幕數(shù)據(jù)
this.timer = null //控制彈幕的開啟和關(guān)
this.barrageVue = barrageVue // 彈幕組件實例
this.deleteCount = 0, // 銷毀彈幕的總數(shù)
this.lastDeleteCount = 0, // 最后可銷毀的數(shù)量
this.row = 0,
this.count = 0
}
/**
* @return 無返回值
* @description 調(diào)用close 方法 關(guān)閉彈幕
*/
close() {
clearInterval(this.timer)
this.removeAllBarrage()
}
/**
* @description 刪除全部的彈幕數(shù)據(jù)
*/
removeAllBarrage() {
this.barrages = []
}
}關(guān)閉功能知識點回顧
在這里我們可以看到,關(guān)閉彈幕的功能其實很簡單,你只需要把開啟彈幕時的定時器關(guān)閉,并且把彈幕數(shù)組數(shù)據(jù)清空就可以了
4.實現(xiàn)彈幕添加功能
index.vue
addBarrage(barrageContent) {
// 獲取當(dāng)前 定時器正在創(chuàng)建的 一行
let currentRow = this.barrageManager.getRow();
let row = currentRow === this.rows / 2 ? 0 : currentRow + 1;
if (row === this.rows / 2) {
row = 0;
}
let myBarrage = {
row,
barrageId: '1686292223004',
barrageContent,
style: this.style,
type: "mySelf", // 用戶自己發(fā)布的彈幕類型
barrageCategory: this.userBarrageType
};
const item = this.crearteBarrageObject(myBarrage);
this.barrageManager.addBarrage(item); // 數(shù)據(jù)準(zhǔn)備好了 調(diào)用添加方法
console.info("發(fā)送成功")
this.barrageManager.setRow(row + 1);
},barrageManager.js
class BarrageManager {
constructor(barrageVue) {
this.barrages = []; // 填彈幕的數(shù)組
this.barragesIds = [] // 批量刪除彈幕的數(shù)組id
this.sourceBarrages = [] // 源彈幕數(shù)據(jù)
this.timer = null //控制彈幕的開啟和關(guān)
this.barrageVue = barrageVue // 彈幕組件實例
this.deleteCount = 0, // 銷毀彈幕的總數(shù)
this.lastDeleteCount = 0, // 最后可銷毀的數(shù)量
this.row = 0,
this.count = 0
}
/**
*
* @param {*} obj 合并完整的的彈幕對象
* @param {...any} args 開發(fā)者以后可能需要傳遞的剩余參數(shù)
*/
addBarrage(obj, ...args) {
const barrage = new Barrage(obj, ...args)
this.barrages.push(barrage)
}
}添加功能知識點回顧
在這里我們可以看到,添加的時候,我們 組件 只需要去調(diào)用 addBarrage 方法進(jìn)行彈幕添加,并且在調(diào)用的過程中我們?nèi)?new Barrage 這個類 , 也就是我們之前準(zhǔn)備好的 彈幕數(shù)據(jù)類 | 數(shù)據(jù)層設(shè)計
5.實現(xiàn)彈幕刪除功能
class BarrageManager {
constructor(barrageVue) {
this.barrages = []; // 填彈幕的數(shù)組
this.barragesIds = [] // 批量刪除彈幕的數(shù)組id
this.sourceBarrages = [] // 源彈幕數(shù)據(jù)
this.timer = null //控制彈幕的開啟和關(guān)
this.barrageVue = barrageVue // 彈幕組件實例
this.deleteCount = 0, // 銷毀彈幕的總數(shù)
this.lastDeleteCount = 0, // 最后可銷毀的數(shù)量
this.row = 0,
this.count = 0
}
/**
*
* @param {*} barrageId // 入?yún)?彈幕id
* @returns 無返回值
* @description 添加需要批量刪除的 id 到 批量刪除的棧中 barragesIds
*/
addBatchRemoveId(barrageId) {
this.barragesIds.push(barrageId)
this.batchRemoveHandle()
}
/**
*
* @param {*} start 你需要從第幾位開始刪除
* @param {*} deleteCount // 刪除的總數(shù)是多少個
* @returns 無返回值
*/
batchRemoveBarrage(start, deleteCount) {
if (this.barrages.length === 0) return
this.barrages.splice(start, deleteCount)
}
batchRemoveId(start, deleteCount) {
if (this.barragesIds.length === 0) return
this.barragesIds.splice(start, deleteCount)
}
/**
* @param {*} barrageId 彈幕 id 針對單個刪除彈幕時 使用
*/
removeBarrage(barrageId) {
let index = this.barrages.findIndex(item => item.barrageId === barrageId)
this.barrages.splice(index, 1)
}
/**
* @description 刪除全部的彈幕數(shù)據(jù)
*/
removeAllBarrage() {
this.barrages = []
}
// 批量移除邏輯處理
batchRemoveHandle() {
if (this.deleteCount === 0 || this.deleteCount === 0) {
if (this.barragesIds.length === this.lastDeleteCount) {
this.batchRemoveBarrage(0, this.lastDeleteCount)
this.batchRemoveId(0, this.lastDeleteCount)
}
} else {
if (this.barragesIds.length === deleteQuantity.FIFTY) {
this.batchRemoveBarrage(0, deleteQuantity.FIFTY)
this.batchRemoveId(0, deleteQuantity.FIFTY)
this.deleteCount--
}
}
}
} 刪除功能知識點回顧
在這里我們可以看到,刪除的時候我們把每一個彈幕id加入到了一個數(shù)組中 , 當(dāng) 彈幕id數(shù)組長度達(dá)到我想要刪除的數(shù)量的時候, 調(diào)用 splice 方法 執(zhí)行批量刪除操作,當(dāng)數(shù)據(jù)發(fā)生更新,視圖也會更新,這樣我們只需要執(zhí)行一次dom操作,不需要每一次刪除彈幕更新dom,造成不必要的性能消耗。
6.實現(xiàn)彈幕重置功能
到這里,我相信你已經(jīng)明白了我的設(shè)計,如果現(xiàn)在讓你實現(xiàn)一個 重置彈幕方法 你會怎么做 ? 是不是只需要,調(diào)用一下 close 方法 , 然后再去 調(diào)用 open方法就可以了,ok 接下來我會將完整版代碼 放入我的github倉庫,小伙伴們可以去拉取 倉庫鏈接,具體代碼還需要小伙伴們自己從頭閱讀一次,這里只是說明了部分內(nèi)容 , 閱讀完成后 , 你就會徹底理解。
關(guān)于 barrageTypeCallback 函數(shù)
這個方法主要是可以解決彈幕樣式定制的問題,你可以根據(jù)每個彈幕的類型 做不同的樣式對象返回,我們會自動幫你渲染。
barrageTypeCallback ( {args} ) {
const { barrageCategary } = args
if(barrageCategary === 'type1'){
retun {
className : 'classOne',
children : {
show : false
i : {
showIcon : false,
style : {
color : 'red'
}
}
}
}
}
else{
return { className : 'default' }
}
}以上就是基于Vue設(shè)計實現(xiàn)一個彈幕組件的詳細(xì)內(nèi)容,更多關(guān)于Vue彈幕組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue-router中hash模式與history模式的區(qū)別
為了構(gòu)建 SPA(單頁面應(yīng)用),需要引入前端路由系統(tǒng),這就是 Vue-Router 存在的意義,而這篇文章主要給大家介紹了關(guān)于vue-router中兩種模式區(qū)別的相關(guān)資料,分別是hash模式、history模式,需要的朋友可以參考下2021-06-06
Vue3?跨域配置devServer的參數(shù)和設(shè)置方法
這篇文章主要介紹了Vue3?跨域配置devServer的參數(shù)和設(shè)置,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04

