vue3.x 实现简单的弹幕功能

思路

  • 定时器定时创建dom元素,
  • 在创建dom的同时添加动画结束监听函数,动画执行完销毁
  • 动画用animation dom创建后自动执行

代码

  • js部分
 // 创建弹幕dom对象
    createBarrage(item) {
      const barrageContent = this.$refs.barrageContent //获取span的父元素
      const barrageSpan = document.createElement("span") //创建span dom
      barrageSpan.style.color = this.getColors() //设置随机颜色
      barrageSpan.innerHTML = item.content //将弹幕列表的内容赋值给弹幕盒子
      barrageSpan.className = "barrage" //动态绑定类名
      //避免前后两次在同一轨道
      var positionRand
      while (this.enterLoop) {
        positionRand = this.getPosition()
        if (this.oldPosition != positionRand) {
          barrageSpan.style.top = positionRand + "%"
          this.oldPosition = positionRand
          break
        }
      }
      // barrageSpan.style.top = this.getPosition() + "%" //设置随机高度

      //弹幕内容过长时提高弹幕速度
      if (item.content.length > 25 && item.content.length < 35)
        barrageSpan.style.animationDuration = "13s"
      else if (item.content.length >= 35)
        barrageSpan.style.animationDuration = "8s"

      barrageContent.appendChild(barrageSpan) //在barrageContent下创建子盒子

      //监听动画结束,回调函数中移除dom元素
      barrageSpan.addEventListener("animationend", function () {
        barrageSpan.remove()
      })
    },
//开始定时器
  mounted() {
    //自动开始定时器,播放弹幕
    //创建弹幕span的定时器
    var barrageListCopy = this.barrageList //创建弹幕副本,以便循环使用
    this.barrageTimer = setInterval(() => {
      if (barrageListCopy.length) {
        let first = barrageListCopy.shift()
        this.createBarrage(first)
      } else {
        // barrageListCopy = this.barrageList
        // 弹幕播放完成后摧毁定时器
        clearInterval(this.barrageTimer)
        this.barrageTimer = null
      }
    }, 2500)
  },

创建dom的函数写在mounted中,因为created钩子中还没有生成dom树,无法获取相应的父元素

  • css部分
@keyframes right2left {
  0% {
    left: 100%;
  }
  100% {
    left: -130%;
  }
}
.barrage {
  display: block;
  position: absolute;
  animation: right2left 16s cubic-bezier(0.44, 0.4, 1, 1) forwards;
  font-size: 1.35vw;
  font-family: Microsoft YaHei UI;
  font-weight: bold;
  color: #4a4a4a;
  /* 不定宽就会导致span一开始很窄 字会强制换行 */
  width: 150vw;
}

大坑!!! 如果使用了饿了么(element ui)插件,要把动画的定义已经新创建的dom的css样式定为全局样式,具体原因懒得查了,这个bug找了好久好久…

### Vue3 Canvas 弹幕插件实现 #### 创建项目结构 为了构建一个基于Vue3和Canvas的弹幕系统,首先需要初始化一个新的Vue3项目并安装必要的依赖项。 ```bash npm init vue@latest cd project-name npm install ``` 接着,在`src/components/`目录下创建名为`DanmakuCanvas.vue`的新组件文件: ```html <template> <div ref="danmakuContainer"></div> </div> </template> <script setup> import { onMounted, ref } from 'vue'; // 定义画布容器引用 const danmakuContainer = ref(null); let canvas; let ctx; onMounted(() => { initializeCanvas(); }); function initializeCanvas() { const containerWidth = window.innerWidth * 0.8; // 设置宽度为视窗宽的80% const containerHeight = window.innerHeight / 4; // 高度设为屏幕高度四分之一 canvas = document.createElement('canvas'); canvas.width = containerWidth; canvas.height = containerHeight; ctx = canvas.getContext('2d'); danmakuContainer.value.appendChild(canvas); drawBackground(); // 初始化背景绘制函数 } </script> ``` 此部分代码实现了基本框架搭建以及Canvas元素初始化[^1]。 #### 设计弹幕逻辑 接下来定义处理弹幕的核心功能——即如何管理弹幕消息队列、防止过多弹幕堆积影响用户体验等问题。这里引入了一个简单的FIFO(先进先出)队列概念来解决“供过于求”的现象[^3]。 ```javascript class DanmakuQueue { constructor(maxSize) { this.queue = []; this.maxSize = maxSize || 50; // 默认最大容量设置为50条 } add(danmakuItem) { if (this.queue.length >= this.maxSize) { this.queue.shift(); // 移除最早加入的一条记录 } this.queue.push(danmakuItem); // 添加新纪录至末尾 } getItems() { return [...this.queue]; // 返回副本而非原数组以防外部修改内部状态 } } export default new DanmakuQueue(75); // 导出实例化对象,默认最多容纳75条评论 ``` 上述类负责维护一条固定长度的消息列表,并提供接口允许其他模块向其中添加新的评论信息。当达到预设上限时自动移除最旧的一项以保持流畅性。 #### 渲染与动画效果 为了让每条弹幕能够按照一定路径移动并在屏幕上展示出来,还需要编写相应的渲染循环及运动算法。 ```javascript function animateDanmakus(items) { items.forEach(item => { item.x -= item.speed; // 向左平移速度单位像素数 if (item.x + item.textWidth < 0) { // 如果超出左侧边界则停止更新该条目位置 return false; } ctx.fillStyle = '#fff'; // 文字颜色设定为白色 ctx.font = `${item.fontSize}px Arial`; ctx.fillText(item.content, item.x, item.y); requestAnimationFrame(() => animateDanmakus([item])); // 请求下一帧继续执行相同操作直至完成整个过程 }); } setInterval(() => { clearCanvas(); // 每次刷新前清除上次绘图痕迹 let activeItems = queue.getItems().filter(i => i.x > -i.textWidth).map((msg, index) => ({ ...msg, y: ((index % Math.floor(canvas.height / msg.fontSize)) + 1) * msg.fontSize + 10 // 计算Y轴坐标避免重叠 })); animateDanmakus(activeItems); }, 16); // 大约每隔16ms触发一次模拟接近于60fps的效果 ``` 这段脚本描述了怎样逐帧调整各条弹幕的位置参数并通过调用`requestAnimationFrame()`方法确保平稳过渡;同时利用间隔定时器定期重新计算所有可见区域内的弹幕布局情况从而形成连续滚动视觉体验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值