基于Vue设计实现一个弹幕组件

 更新时间:2023年06月13日 11:15:19   作者:前端小张同学  
这篇文章主要给大家分享一个开发中常见的需求,接下来将为大家详细介绍弹幕的实现以及设计思路一步一步描述出来,希望大家能够喜欢

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弹幕组件的资料请关注脚本之家其它相关文章!

相关文章

  • vue学习笔记之slot插槽用法实例分析

    vue学习笔记之slot插槽用法实例分析

    这篇文章主要介绍了vue学习笔记之slot插槽用法,结合实例形式对比分析了vue使用slot插槽的相关操作技巧与注意事项,需要的朋友可以参考下
    2020-02-02
  • 浅析vue cli3 封装Svgicon组件正确姿势(推荐)

    浅析vue cli3 封装Svgicon组件正确姿势(推荐)

    这篇文章主要介绍了vue cli3 封装Svgicon组件正确姿势,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • vue-router3.x和vue-router4.x相互影响的问题分析

    vue-router3.x和vue-router4.x相互影响的问题分析

    这篇文章主要介绍了vue-router3.x和vue-router4.x相互影响的问题分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • vue3 setup语法糖下父组件如何调用子组件

    vue3 setup语法糖下父组件如何调用子组件

    这篇文章主要介绍了vue3 setup语法糖下父组件如何调用子组件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • 关于Echarts饼图图例太长的解决方案

    关于Echarts饼图图例太长的解决方案

    这篇文章主要介绍了关于Echarts饼图图例太长的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 关于eslint+prettier+husky的配置及说明

    关于eslint+prettier+husky的配置及说明

    这篇文章主要介绍了关于eslint+prettier+husky的配置及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • 超详细动手搭建一个VuePress 站点及开启PWA与自动部署的方法

    超详细动手搭建一个VuePress 站点及开启PWA与自动部署的方法

    这篇文章主要介绍了超详细动手搭建一个VuePress 站点及开启PWA与自动部署的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • Vue双向绑定实现原理与方法详解

    Vue双向绑定实现原理与方法详解

    这篇文章主要介绍了Vue双向绑定实现原理与方法,结合实例形式详细分析了发布者-订阅者模式、脏值检查、数据劫持与双向绑定相关实现技巧,需要的朋友可以参考下
    2020-05-05
  • vue中使用Tinymce的示例代码

    vue中使用Tinymce的示例代码

    这篇文章主要介绍了vue中使用Tinymce的示例,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • vue里面使用mui的弹出日期选择插件实例

    vue里面使用mui的弹出日期选择插件实例

    今天小编就为大家分享一篇vue里面使用mui的弹出日期选择插件实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09

最新评论