在线客服

如果让你设计一个弹幕组件你会怎么做

adminadmin 报建百科 2024-04-25 92 13
如果让你设计一个弹幕组件你会怎么做

大家好,我是前端小张同学,这次给大家更新一个开发中常见的需求,接下来我会将我的弹幕实现以及设计思路一步一步描述出来,并分享给大家,希望大家喜欢。

今天我们的主题是 ,用vue手写一个弹幕

1:关于弹幕设计思想

1.1 : 业务层 | 视图层(全局组件)

1.1.1 : 从业务角度来说,如果你设计的是全局弹幕组件,你要考虑以下几点。

  1. 容器的高度?
  2. 容器层次结构划分?
  3. 渲染弹幕的方式,使用组件的人应该传递什么数据?
  4. 是否支持全屏弹幕?
  5. 是否支持弹幕关闭和开启?
  6. 是否需要重置弹幕?
  7. 是否支持暂停弹幕?
  8. 是否需要集成发送功能?

设计方案考虑完整了以后,你将可以开始考虑 数据层的设计

1.2 数据层

1.2.1 : 从数据角度来说每一条弹幕无非是一个element,然后把弹幕内容放到这个element元素中,并且给 element 添加动画,那接下来,你应该这样考虑。

  1. 弹幕是JS对象?它的属性有哪些?

  2. 谁去管理这些弹幕?如何让他能够支持暂停和关闭?

  3. 你如何把后台的数据,与你前台的一些静态数据进行合并,创造出一个完整对象?

  4. 你怎么去渲染这些弹幕?

  5. 你想要几秒创建一次弹幕并在容器内显示和运行?

  6. 弹幕具备哪些灵活的属性?
    运行动画时间 , 用户自己发布的弹幕样式定制? 又或者,弹幕需要几条弹道内运行等等这些你都需要考虑。

数据设计方案考虑完整了以后,你将可以开始考虑 数据管理层的设计

1.3 数据管理层

1.3.1 从管理的角度来说,外界调用某些方法,你即可快速的响应操作,例如外界调用 open 方法,你就播放弹幕,调用Stop方法,你就关闭弹幕 接下来,你应该考虑以下几点。

  1. 面向对象设计,应该提供哪些方法,具备哪些功能?
  2. 调用了指定的方法,应该怎么对数据进行操作。
  3. 如何对弹幕做性能优化?

到这里 , 我们设计方案基本完成,接下来我们可以开始编写代码。

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.1 : 数据管理层设计方案实现

2.1.1 :我们在这里实现了 , 弹幕的 增加删除初始化重置关闭开启功能

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 秒创建一条弹幕,加入到弹幕数组中。

  1. 连接视图层

2.1 : 视图层 | 业务层设计方案实现

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,造成不必要的性能消耗。

5: 实现弹幕重置功能

到这里,我相信你已经明白了我的设计,如果现在让你实现一个 重置弹幕方法 你会怎么做 ? 是不是只需要,调用一下 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' }
 }
}



结束语

前面的所有代码只是想告诉大家这个设计思想,当你的思维模型出来以后,其实很轻松。

我是 前端小张同学,期待你的关注,谢谢。

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!
代办报建

本公司承接江浙沪报建代办施工许可证。
联系人:张经理,18321657689(微信同号)。

喜欢0发布评论

13条评论

  • 游客 发表于 2个月前

    最近回了很多帖子,都没人理我!http://www.dnf70.com/1087.html

  • 稳定计划专业回血 发表于 1个月前

    世界末日我都挺过去了,看到楼主我才知道为什么上帝留我到现在!http://5n47.pco9.com

发表评论

  • 昵称(必填)
  • 邮箱
  • 网址