vue3实现弹幕

弹幕动画效果

一般来说我们认知的动画流畅度顺序是 css(transform) > css(left) > js

功能说明:

该示例弹道为5,用户第一次发送的弹幕出现在第3个弹道,循环播放弹幕列表,利用vue更新当前执行列表刷新dom,随机生成颜色和字重及速度,动画结束是监视 animationend 的回调,因为此处动画时长是固定1000ms,所以每次都移除第一个 DOM 即可,此示例会出现覆盖现象,页末有补充资料,此文只做简单演示。B站弹幕将样式直接附在dom上,可做参考。

image-20220706160113738

实现代码:

<template>
  <div>
    <div class="bullet-wrap">
      <div
        class="bullet-item"
        :class="
          item.isImportant
            ? 'bold bullet-item-color-' +
              item.color +
              ' bullet-animation-' +
              item.random
            : 'bullet-item-color-' +
              item.color +
              ' bullet-animation-' +
              item.random
        "
        :data-line="item.line"
        v-for="item in state.bulletlist"
        @animationend="animationend"
        :key="item.id"
      >
        {{ item.name }}
      </div>
    </div>
    <div class="flex">
      <input
        style="flex: 1 1 auto"
        type="text"
        ref="input"
        v-model="state.values"
      />
      <button style="flex: 0 1 3em; text-align: center" @click="sendBullet">
        发送
      </button>
    </div>
  </div>
</template>
<script>
import { reactive, onMounted } from "vue";

export default {
  setup() {
    function getUUID() {
      return Date.now() + Math.random();
    }
    const state = reactive({
      list: [
        {
          id: getUUID(),
          name: "弹幕即将来袭~",
          isLoginUser: true,
          isImportant: true,
          color: 0,
          random: 0,
          line: 0,
        },
        {
          id: getUUID(),
          name: "2222222",
          isLoginUser: true,
          isImportant: false,
          color: 1,
          random: 1,
          line: 0,
        },
        {
          id: getUUID(),
          name: "3333",
          isLoginUser: true,
          isImportant: true,
          color: 1,
          random: 2,
          line: 0,
        },
      ], // 普通的弹幕队列
      clist: [], // c位的弹幕队列
      bulletlist: [], // 当前正在执行的
      values: "",
      idx: 3,
    });

    function animationend() {
      state.list.push(state.bulletlist.shift());
    }

    function sendBullet() {
      if (state.values) {
        // 随机生成重要性和颜色
        state.clist.push({
          id: getUUID(),
          name: state.values,
          isLoginUser: true,
          isImportant: Math.random() >= 0.5,
          color: parseInt(Math.random() * (4 - 0 + 1) + 0, 10), 
          random: parseInt(Math.random() * (3 - 0 + 1) + 0, 10),
          line: 0,
        });
        state.values = "";
      }
    }

    onMounted(() => {
      setInterval(() => {
        var item = null;
        if (state.idx == 3) {
          // c 位
          item = state.clist.shift();
        }
        if (!item) {
          item = state.list.shift();
        }
        if (item) {
          item.line = state.idx;
          state.idx = (state.idx % 5) + 1;
          state.bulletlist.push(item);
        } else {
          if (state.clist.length) {
            item = state.clist.shift();
            item.line = 3;
            state.idx = 3;
            state.bulletlist.push(item);
          }
        }
      }, 1000);
    });

    return {
      state,
      sendBullet,
      animationend,
    };
  },
};
</script>
<style scoped>
.flex {
  display: flex;
}

.van-cell-group--inset {
  margin: auto;
  border-radius: 50px;
  border: 1px solid #d8d7d7;
}
.van-cell {
  background: #f8f8f8;
}
.bold {
  font-weight: bold;
}
.bullet-wrap {
  height: 400px;
  position: relative;
  overflow: hidden;
  background: #333;
  background-size: cover;
  margin-bottom: 40px;
}
.bullet-item {
  text-shadow: 1px 1px #000;
  color: #ffffff82;
  white-space: nowrap;
  user-select: none;
  position: absolute;
  top: 0;
  left: 0;
}
.bullet-item-color-0 {
  color: rgb(71, 104, 244);
}
.bullet-item-color-1 {
  color: rgb(231, 59, 80);
}
.bullet-item-color-2 {
  color: rgb(255, 121, 0);
}
.bullet-item-color-3 {
  color: rgb(18, 195, 153);
}
.bullet-item-color-4 {
  color: rgb(100, 104, 102);
}
.bullet-item[data-line="1"] {
  top: 0px;
}
.bullet-item[data-line="2"] {
  top: 80px;
}
.bullet-item[data-line="3"] {
  top: 160px;
}
.bullet-item[data-line="4"] {
  top: 240px;
}
.bullet-item[data-line="5"] {
  top: 320px;
}
.bullet-animation-0 {
  animation: right2left 6s linear both;
}
.bullet-animation-1 {
  animation: right2left 4s linear both;
}
.bullet-animation-2 {
  animation: right2left 8s linear both;
}
.bullet-animation-3 {
  animation: right2left 5s linear both;
}

@keyframes right2left {
  0% {
    transform: translate(100vw);
  }
  100% {
    transform: translate(-100%);
  }
}
</style>

补充资料

[1] 前端js实现弹幕,怎么控制弹幕不重叠,有什么思路吗?

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现弹幕功能需要借助一些库,这里以使用 `DPlayer` 播放器和 `Danmaku` 弹幕库为例进行讲解。 1. 安装 `DPlayer` 和 `Danmaku`: ```bash npm install dplayer danmaku ``` 2. 在 Vue 中引入 `DPlayer` 和 `Danmaku`: ```vue <template> <div class="player-container"> <div ref="player"></div> <div class="danmaku-input"> <input type="text" v-model="danmakuText" placeholder="请输入弹幕内容" /> <button @click="sendDanmaku">发送</button> </div> </div> </template> <script> import DPlayer from 'dplayer'; import Danmaku from 'danmaku'; export default { data() { return { danmakuText: '', danmaku: null, }; }, mounted() { this.initPlayer(); }, methods: { // 初始化 DPlayer 播放器和 Danmaku 弹幕库 initPlayer() { const options = { video: { url: 'your_video_url', }, }; const player = new DPlayer({ container: this.$refs.player, ...options, }); this.danmaku = new Danmaku(player.video, player.danmaku, options); }, // 发送弹幕 sendDanmaku() { if (!this.danmakuText) return; const danmakuItem = { type: 'right', // 弹幕位置 text: this.danmakuText, // 弹幕内容 color: '#fff', // 弹幕颜色 time: this.danmaku.video.currentTime, // 弹幕出现时间 }; this.danmaku.send(danmakuItem); this.danmakuText = ''; }, }, }; </script> ``` 3. 在 `sendDanmaku` 方法中,我们创建一个弹幕对象 `danmakuItem`,设置弹幕的位置、内容、颜色和出现时间,然后调用 `Danmaku` 实例的 `send` 方法发送弹幕。 注意:在上面的代码中,`your_video_url` 需要替换为你的视频地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sea_lichee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值