前言
之前一直想实现一下弹幕效果,因为种种原因(ps:哈哈,就是太懒了)一直没有做。正好最近比较闲,因此打算来实现一下,先简单描述下具体效果,页面展示一段视频,视频下方有一个文本输入框,可以输入弹幕,输入完成之后,弹幕轮播显示在视频上方。
页面结构
分为视频和弹幕输入两部分,视频部分包含了视频标签和展示弹幕的文字层,都使用绝对定位,并让弹幕显示在视频上面(z-index控制层级,数值越大,位置就在最上面)。弹幕输入部位包含一个文本输入框和按钮
ini
<div class="container">
<div id="barrage-container"></div>
<video src="/video.mp4" controls ></video>
</div>
<div class="send-box">
<input id="send-input" type="text" placeholder="请输入弹幕内容">
<button id="send-btn">发送</button>
</div>
初步尝试
弹幕的简单运行逻辑:输入弹幕之后,点击发送,弹幕就从视频上面的最左边移动到最右边并消失
因此好像可以实现css3的动画属性实现弹幕移动效果,先简单尝试一下。给发送按钮添加点击事件,如果输入框不存在内容,直接return出去。有值再创建一个div元素,并把输入框的文本内容赋值给div元素,添加元素类名,在css中设置动画。再设置一个范围内的随机值作为div元素的top值(创建元素的代码在下面展示)。最后把div元素添加到弹幕模块中,并清空输入框的值。
注:每个弹幕元素的动画执行完成后,可以移除该弹幕元素,及时释放不再需要的 DOM 节点,这样可以减少页面的内存占用,提高页面的整体性能
ini
const barrageContainer = document.getElementById('barrage-container')
const input = document.getElementById('send-input');
const button = document.getElementById('send-btn');
button.addEventListener('click', function () {
let value = input.value.trim()
if(!value){
return
}
let dom = createElement(value, true)
barrageContainer.appendChild(dom)
input.value = ''
});
实现弹幕移动动画,弹幕元素使用绝对定位,并改变left值和transform在水平方向上的移动距离。
这样可以简单实现输入弹幕,并展示的效果。但是没有考虑到原本有弹幕数据展示的情况。
功能改善
首先模拟写了一些假数据(ps:正常情况是请求后台的弹幕数据展示,估计应该就复杂了,这里就纯前端展示了)。创建一个初始化弹幕的函数,用来循环这些数据,创建弹幕元素。因为要创建多个元素,所以使用了创建文档片段的方法document.createDocumentFragment()
,等元素都添加完成之后,一次性添加到弹幕模块中。
js
function initBarrage() {
// 创建一个文档片段(不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染)
let frag = document.createDocumentFragment();
data.forEach((item) => {
let domItem = createElement(item.text, false)
// 将所有节点添加到这个文档片段中
frag.appendChild(domItem);
})
// 将这个文档片段一次性添加到元素中
barrageContainer.appendChild(frag);
}
封装创建弹幕元素的方法,以便于在一开始加载弹幕数据和输入弹幕的时候使用。在该方法中,初始加载弹幕需要设置动画的延迟时间,不然弹幕会重叠在一起,同时监听弹幕元素动画,在动画开始时才显示。
之前随机获取的弹幕位置的top值,如果在有弹幕显示的情况下,再发送弹幕,随机top值可能会重复,造成弹幕元素重叠。因此定义一个数组来跟踪当前正在显示的弹幕的top值,然后在生成新的top时,检查数组中是否包含该top值。如果存在,需要重新生成一个新的top值。最后,在弹幕动画结束时,我们从该数组中移除对应的top值,以便后续的弹幕可以使用这个值。
js
let interval = 1 //动画延迟时间
let currentHeights = []; //获取显示的弹幕top值
function createElement(value, isSend) {
let barrage = document.createElement('div')
barrage.textContent = value
barrage.className = 'barrage'
barrage.style.top = getRandomTop(5, barrageHeight * 0.6) + 'px'
if (!isSend) {
barrage.style.animationDelay = interval + 's'
barrage.style.opacity = 0
barrage.addEventListener('animationstart', function () {
barrage.style.opacity = 1
})
interval += 2
}
barrage.addEventListener('animationend', function () {
barrageContainer.removeChild(barrage);
currentHeights = currentHeights.filter(item => item !== parseInt(barrage.style.top));
})
return barrage
}
//随机获取高度
function getRandomTop(start, end) {
let top;
do {
let randomNumber = start + Math.random() * (end - start)
top = parseFloat(randomNumber.toFixed(2));
} while (currentHeights.includes(top))
currentHeights.push(top);
return top;
}
按照以上代码就简单实现了弹幕效果。当然还有很多问题,比如弹幕速度的控制、弹幕分布密度、弹幕风格、页面性能等,看来还有的研究啊。