1.弹幕
弹幕(Danmaku)是一种源自日本的视频评论方式,它允许观众在视频播放时实时发表评论,这些评论会以弹幕的形式直接出现在视频画面上。这种评论方式最初是由日本的一个动画分享网站Niconico动画(Niconico Douga)在2006年引入的,后来被中国的AcFun和Bilibili等视频网站采用并推广。
类似下图中的效果:
2.如何实现
弹幕的本质是滚动,从屏幕一侧飞向另一侧,这里我们以最常见的从右至左的弹幕形势为例。 也就是说,要实现一条弹幕至少要分为以下三步:
- 确定弹幕的
初始位置
,一般是在屏幕之外 - 确定弹幕的
终止位置
,一般是屏幕的另外一侧 - 使用动画实现弹幕的移动动画
3.步骤详解
接下来,我们演示一下在Vue3
中实现一个简单的弹幕,效果不是重点,重点是帮助大家理解弹幕的基本生成逻辑和原理。
3-1. 生成弹幕数据
import { useIntervalFn } from "@vueuse/core";
import { fakerZH_CN as faker } from '@faker-js/faker';
// 弹幕模型定义
interface BarrageItem {
id: string; // ID
avatar: string; // 头像
name: string; // 用户名
content: string; // 弹幕内容
}
// 配置
const config = ref({
stay: 100, // 弹幕停留时间,ms
animate: 15, // 弹幕滚动时间,s
speed: 1000, // 弹幕产生速度, ms
track: [10, 80, 150, 220, 290, 360] , // 轨道列表
})
// 弹幕列表
const barrageList: Ref<BarrageItem[]> = ref([]);
// 模拟自动生成弹幕数据
const { pause, resume } = useIntervalFn(
() => {
const barrage: BarrageItem = {
id: faker.string.uuid(),
avatar: faker.image.avatar(),
name: faker.person.fullName(),
content: faker.music.songName()
}
console.log("barrage: ", barrage);
barrageList.value.push(barrage);
},
config.value.speed, {
immediate: false // 手动控制定时器
}
);
- 首先我们定义了一个弹幕的数据类型
- 然后添加一个弹幕的配置项,方便后续进行调试;这里包括了弹幕的生成速度,滚动时间,停留时间以及弹幕的轨道
- 最后使用了Hook
useIntervalFn
来实现一个定时任务,循环产生一条弹幕数据
3-2. 渲染弹幕
<!---弹幕容器-->
<section class="relative outline-(dashed 1px blue) h-460px w-1024px">
<section class="flex flex-row justify-center h-full">
<TransitionGroup tag="ul" name="slide" class="overflow-hidden w-full h-full relative" @enter="barrageEnter">
<!---单个弹幕-->
<li v-for="item in barrageList" :key="item.id" class="barrage-item">
<img :src="item.avatar" class="w-50px h-50px rounded-full mr-2 border-(2px solid [#fdcd00])" />
<p class="text-lg font-bold">
<span class="mr-2" v-text="item.name + ':'"></span>
<span v-text="item.content"></span>
</p>
</li>
</TransitionGroup>
</section>
</section>
使用Vue的v-for
循环生成弹幕Dom,同时添加元素的样式。
这里最主要的是监听了TransitionGroup
的enter事件,@enter="barrageEnter"
3-3. 随机轨道
// 轨道列表
const trackIndex = ref(-1);
// 打乱轨道
const trackList = _.shuffle(config.value.track);
const TRACK_NUM = trackList.length;
// 随机获取定位
const getPositionRandom = () => {
let idx = trackIndex.value + 1;
if (idx >= TRACK_NUM) {
idx = 0;
}
trackIndex.value = idx;
return trackList[trackIndex.value] + _.random(0, 10);
};
// 此回调函数是可选项的设置
// 与 CSS 结合时使用
const barrageEnter = (ele: Element) => {
const el = ele as HTMLElement
el.style.top = `${getPositionRandom()}px`;
el.style.animationDuration = `${config.value.animate}s`;
// 弹幕动画结束后,删除DOM元素
el.addEventListener("animationend", () => {
setTimeout(() => {
el.parentNode?.removeChild(el);
}, config.value.stay);
});
};
在enter事件中,我们为弹幕元素随机设置了一个top值,也就是轨道的位置。 同时,监听元素动画结束后,自动将其删除,避免累积过多无用的DOM元素占用内存。
3-4 弹幕动画
@keyframes barrage {
0% {
transform: translateX(1024px) translateZ(0);
}
100% {
transform: translateX(-100%) translateZ(0);
}
}
.barrage-item {
position: absolute;
line-height: 1;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
animation-name: barrage;
animation-timing-function: linear;
transform: translateX(1024px);
}
定义了一个关键帧动画barrage
,将元素从1024px(弹幕容器的宽度)移动到-100%(弹幕自身的宽度)。
这里只是做一个最简单的参考,大家可以根据实际需求进行调整,添加更为复杂的动画效果。
4. 运行项目
// 自动开启弹幕
onMounted(() => resume())
// 关闭定时器
onBeforeUnmount(() => pause())
我们在vue组件渲染时,自动开始产生弹幕,同时在页面卸载时取消定时器。 自此,弹幕效果基本上开发完成,运行后即可看到效果。
5.参考源码
以上全部的源码已经托管在Gitee上,感兴趣的同学可以自取。