基于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ù)進行合并,創(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ù)進行操作。
- 如何對彈幕做性能優(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
方法進行彈幕添加,并且在調(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ù)組長度達到我想要刪除的數(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)一個彈幕組件的詳細內(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-06Vue3?跨域配置devServer的參數(shù)和設(shè)置方法
這篇文章主要介紹了Vue3?跨域配置devServer的參數(shù)和設(shè)置,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04