基于Vue设计实现一个弹幕组件
作者:前端小张同学
1.关于弹幕设计思想
1.1业务层 | 视图层(全局组件)
从业务角度来说,如果你设计的是全局弹幕组件,你要考虑以下几点。
- 容器的高度?
- 容器层次结构划分?
- 渲染弹幕的方式,使用组件的人应该传递什么数据?
- 是否支持全屏弹幕?
- 是否支持弹幕关闭和开启?
- 是否需要重置弹幕?
- 是否支持暂停弹幕?
- 是否需要集成发送功能?
设计方案考虑完整了以后,你将可以开始考虑 数据层的设计
1.2数据层
从数据角度来说每一条弹幕无非是一个element
,然后把弹幕内容放到这个element
元素中,并且给 element
添加动画,那接下来,你应该这样考虑。
- 弹幕是JS对象?它的属性有哪些?
- 谁去管理这些弹幕?如何让他能够支持暂停和关闭?
- 你如何把后台的数据,与你前台的一些静态数据进行合并,创造出一个完整对象?
- 你怎么去渲染这些弹幕?
- 你想要几秒创建一次弹幕并在容器内显示和运行?
- 弹幕具备哪些灵活的属性?运行动画时间 , 用户自己发布的弹幕样式定制? 又或者,弹幕需要几条弹道内运行等等这些你都需要考虑。
数据设计方案考虑完整了以后,你将可以开始考虑 数据管理层
的设计
1.3数据管理层
从管理的角度来说,外界调用某些方法,你即可快速的响应操作,例如外界调用 open
方法,你就播放弹幕,调用Stop
方法,你就关闭弹幕 接下来,你应该考虑以下几点。
- 面向对象设计,应该提供哪些方法,具备哪些功能?
- 调用了指定的方法,应该怎么对数据进行操作。
- 如何对弹幕做性能优化?
到这里 , 我们设计方案基本完成,接下来我们可以开始编写代码。
2.代码实现
2.1数据层设计方案实现
我们需要构建一个 Barrage
类 ,我们每次去创建一个弹幕的时候都会 new Barrage
,让他帮助我们生成一些弹幕属性。
export class Barrage { constructor(obj) { // 每次 new Barrage() 传入一个 后台返回的数据对象 obj const { barrageId, speed, level, top, jumpUrl, barrageContent, animationPlayState, ...args } = obj this.barrageId = barrageId; // id : 每条弹幕的唯一id this.speed = speed; // speed : 弹幕运行的速度,由外界控制 this.level = level; // level : 弹幕的层级 --> 弹幕可分为设计可分为 上下 1 , 1 两个层级 ,可决定弹幕的显示是全屏还是半屏显示 this.top = top; // top :弹幕生成的位置相对于 level 的层级 决定 ,相对于 Level 层级 盒子距离顶部的位置 this.jumpUrl = jumpUrl; // jumpUrl :点击弹幕需要跳转的链接 this.barrageContent = barrageContent; // barrageContent : 弹幕的内容 this.animationPlayState = ''; // 设计弹幕 是否可 点击暂停功能 this.color = '#FFF' // 弹幕颜色 this.args = args // 除去Barrage类之外的一些数据属性全部丢到这里,例如后台返回的数据 } }
2.2数据管理层设计方案实现
我们在这里实现了 , 弹幕的 增加,删除,初始化,重置,关闭 , 开启功能
1.实现弹幕开启功能
BarrageManager.js
export class BarrageManager { constructor(barrageVue) { this.barrages = []; // 填弹幕的数组 this.barragesIds = [] // 批量删除弹幕的数组id this.sourceBarrages = [] // 源弹幕数据 this.timer = null //控制弹幕的开启和关 this.barrageVue = barrageVue // 弹幕组件实例 this.deleteCount = 0, // 销毁弹幕的总数 this.lastDeleteCount = 0, // 最后可销毁的数量 this.row = 0, this.count = 0 } init(barrages) { this.sourceBarrages = barrages this.deleteCount = parseInt(this.sourceBarrages.length / deleteQuantity.FIFTY) // 计算可删除数量 this.lastDeleteCount = this.sourceBarrages.length % deleteQuantity.FIFTY // 计算 最后一次可删除数量 } /** * * @param {*} barrages 接收一个弹幕数组数据 * @description 循环创建 弹幕对象 ,将后台数据与 创建弹幕的属性结合 存入弹幕数组 */ loopCreateBarrage(barrages) { const { rows, createTime, crearteBarrageObject } = this.barrageVue let maxRows = rows / 2 // 最大的弹幕行数 this.timer = setInterval(() => { for (let i = 0; i < 1; i++) { let barrageItem = barrages[this.count] if (this.row >= maxRows) { this.row = 0 } // 如果当前已经到了 最大的弹幕行数临界点则 回到第0 行弹道继续 创建 if (!barrageItem) return clearInterval(this.timer) // 如果取不到了则证明没数据了 , 结束弹幕展示 const item = crearteBarrageObject({ row: this.row, ...barrageItem }) // 添加对象到 弹幕数组中 this.addBarrage(item) this.count++ // 用于取值 ,取了多少条 this.row++ // 用于弹道 } }, createTime * 1000); } /** * @param {*} barrages 传入一个弹幕数组数据 * @returns 无返回值 * @description 调用 该方法 开始播放弹幕 */ open(barrages) { if (barrages.length === 0) return this.init(barrages) this.loopCreateBarrage(this.sourceBarrages) } }
在这里我们初始化了一个 open 方法,并接收一个数组 ,并调用了 init
方法 去做初始化操作,并调用了 循环创建的方法,没 createTime 秒创建一条弹幕,加入到弹幕数组中。
2.连接视图层
2.3视图层 | 业务层设计方案实现
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 和 安卓 触发点击事件 --> <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() { // 初始化弹幕渲染数据 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 ) || [] }; }, } }
视图层知识点回顾
在这里我们在弹幕组件创建的时候去创建了一个 弹幕管理对象
,并且在挂载的时候去初始化了以下 弹幕渲染的数据,于是我们调用了 弹幕管理类
的 open
方法,这样当组件挂载时,就会去渲染 barrageFiltering
数据,这里我们是在管理类中拿到了管理类中
循环创建的数据。
open 方法实现
到这里我们的弹幕的开启基本上已经完成了,可以看得出,如果你是这样设计的,你只需要在组件中调用管理类的一些方法,它就能帮你完成一些功能。
3.实现弹幕关闭功能
barrageManager.js
class BarrageManager { constructor(barrageVue) { this.barrages = []; // 填弹幕的数组 this.barragesIds = [] // 批量删除弹幕的数组id this.sourceBarrages = [] // 源弹幕数据 this.timer = null //控制弹幕的开启和关 this.barrageVue = barrageVue // 弹幕组件实例 this.deleteCount = 0, // 销毁弹幕的总数 this.lastDeleteCount = 0, // 最后可销毁的数量 this.row = 0, this.count = 0 } /** * @return 无返回值 * @description 调用close 方法 关闭弹幕 */ close() { clearInterval(this.timer) this.removeAllBarrage() } /** * @description 删除全部的弹幕数据 */ removeAllBarrage() { this.barrages = [] } }
关闭功能知识点回顾
在这里我们可以看到,关闭弹幕的功能其实很简单,你只需要把开启弹幕时的定时器关闭,并且把弹幕数组数据清空就可以了
4.实现弹幕添加功能
index.vue
addBarrage(barrageContent) { // 获取当前 定时器正在创建的 一行 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", // 用户自己发布的弹幕类型 barrageCategory: this.userBarrageType }; const item = this.crearteBarrageObject(myBarrage); this.barrageManager.addBarrage(item); // 数据准备好了 调用添加方法 console.info("发送成功") this.barrageManager.setRow(row + 1); },
barrageManager.js
class BarrageManager { constructor(barrageVue) { this.barrages = []; // 填弹幕的数组 this.barragesIds = [] // 批量删除弹幕的数组id this.sourceBarrages = [] // 源弹幕数据 this.timer = null //控制弹幕的开启和关 this.barrageVue = barrageVue // 弹幕组件实例 this.deleteCount = 0, // 销毁弹幕的总数 this.lastDeleteCount = 0, // 最后可销毁的数量 this.row = 0, this.count = 0 } /** * * @param {*} obj 合并完整的的弹幕对象 * @param {...any} args 开发者以后可能需要传递的剩余参数 */ addBarrage(obj, ...args) { const barrage = new Barrage(obj, ...args) this.barrages.push(barrage) } }
添加功能知识点回顾
在这里我们可以看到,添加的时候,我们 组件 只需要去调用 addBarrage
方法进行弹幕添加,并且在调用的过程中我们去 new Barrage
这个类 , 也就是我们之前准备好的 弹幕数据类 | 数据层设计
5.实现弹幕删除功能
class BarrageManager { constructor(barrageVue) { this.barrages = []; // 填弹幕的数组 this.barragesIds = [] // 批量删除弹幕的数组id this.sourceBarrages = [] // 源弹幕数据 this.timer = null //控制弹幕的开启和关 this.barrageVue = barrageVue // 弹幕组件实例 this.deleteCount = 0, // 销毁弹幕的总数 this.lastDeleteCount = 0, // 最后可销毁的数量 this.row = 0, this.count = 0 } /** * * @param {*} barrageId // 入参 弹幕id * @returns 无返回值 * @description 添加需要批量删除的 id 到 批量删除的栈中 barragesIds */ addBatchRemoveId(barrageId) { this.barragesIds.push(barrageId) this.batchRemoveHandle() } /** * * @param {*} start 你需要从第几位开始删除 * @param {*} deleteCount // 删除的总数是多少个 * @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 删除全部的弹幕数据 */ 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加入到了一个数组中 , 当 弹幕id数组长度达到我想要删除的数量的时候, 调用 splice
方法 执行批量删除操作,当数据发生更新,视图也会更新,这样我们只需要执行一次dom操作,不需要每一次删除弹幕更新dom,造成不必要的性能消耗。
6.实现弹幕重置功能
到这里,我相信你已经明白了我的设计,如果现在让你实现一个 重置弹幕方法 你会怎么做 ? 是不是只需要,调用一下 close
方法 , 然后再去 调用 open
方法就可以了,ok 接下来我会将完整版代码 放入我的github仓库,小伙伴们可以去拉取 仓库链接,具体代码还需要小伙伴们自己从头阅读一次,这里只是说明了部分内容 , 阅读完成后 , 你就会彻底理解。
关于 barrageTypeCallback 函数
这个方法主要是可以解决弹幕样式定制的问题,你可以根据每个弹幕的类型 做不同的样式对象返回,我们会自动帮你渲染。
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设计实现一个弹幕组件的详细内容,更多关于Vue弹幕组件的资料请关注脚本之家其它相关文章!