Bootstrap

h5移动端循环弹幕动画实现

需求

有一个不定长度的数组,数组中是弹幕的文字内容,移动到弹幕页面时从数组第一项开始循环滚动,与顶部距离随机、两条弹幕间隔时间随机、弹幕速度随机。先看一下最终效果:
最终效果

思路

  1. 做好一个div的弹幕动画效果,记下影响其高度、速度的css属性
  2. 循环遍历数组,通过定时器函数根据不同间隔依次添加dom元素
  3. 递归进行函数调用,并且在离开页面时销毁所有定时器、清空弹幕div

代码

首先先做一个带动画的div,编写动画keyframes,其实动画就是从屏幕的最右侧到屏幕的最左侧加上弹幕自身宽度的距离,这个距离是可以通过calc(-100vw - 100%)计算出来。这时我们可以注意到,影响高度、速度的主要是topanimation中的那个6s,也就是完成动画所需时间,时间越短速度越快。

html代码

<div class='bullet-chat-box'>
	<div class='dan'>德玛西亚德玛西亚德玛西亚德玛西亚</div>
</div>
<style>
.bullet-chat-box{
    width: 100%;
    height: 50%;
}
.dan{
    position: absolute;
    left: 100%;
    background: #333;
    white-space: nowrap;
    padding: 0 14px;
    border-radius: 50px;
    height: 24px;
    line-height: 24px;
    color: #fff;
    letter-spacing: 2px;
    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.2);
    cursor: default;
    top: 80px;
    animation: danmu 6s linear 0s infinite;
}
@-webkit-keyframes danmu {
    from {
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }
    to {
        -webkit-transform: translateX(calc(-100vw - 100%));
        transform: translateX(calc(-100vw - 100%));
    }
}
@keyframes danmu {
    from {
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }
    to {
        -webkit-transform: translateX(calc(-100vw - 100%));
        transform: translateX(calc(-100vw - 100%));
    }
}
</style>

单独的动画效果已经做出来了,并且也知道了影响高度、速度的css属性,我们就可以进行下一步,也就是遍历弹幕数组并通过定时器依次渲染出来。

js代码

开始进行数组遍历,这里首先随机了动画时间、距离顶部高度、两条弹幕间的时间间隔,之后用到了await配合promise + setTimeout()进行延时休眠,记录好生成的随机时间间隔,并延时对应时间之后将弹幕div添加到dom中,然后再次延时对应动画运行时间后根据class删除对应的dom元素,判断是最后一个弹幕时再次递归调用,实现了循环弹幕,代码如下:

// 用了jquery,记得引入
var timers = [],
	bulletChat = ['德玛西亚德玛西亚', '111222'], // 弹幕数组
	bulletChatTime = 0;
	
// 延时函数
function sleep(interval){
  return new Promise((resolve)=>{
    let timer = setTimeout(resolve, interval)
    timers.push(timer)
  });
}
// 开启弹幕
function bulletChatStart(i=1){
  bulletChat.forEach(async (item, index) => {
    // 动画时间随机范围,单位s
    let numMax = 10, numMin = 3;
    // 两条弹幕之间时间间隔随机范围,单位100ms
    let bulletChatMax = 20, bulletChatMin = 5;
    // 动画时间
    let num = Math.floor(Math.random() * (numMax - numMin + 1) + numMin)
    // 距离顶部高度百分比
    let top = Math.floor(Math.random() * (35 - 2 + 1) + 2)
    if (index !== 0) {
      bulletChatTime += Math.floor(Math.random() * (bulletChatMax - bulletChatMin + 1) + bulletChatMin)
    }
    await sleep(100 * bulletChatTime)
    $('.bullet-chat-box').append(`<div class="dan dan-${index + i}" style="animation: danmu ${num}s linear 0s infinite;top: ${top}%;">${item}</div>`)
    await sleep(1000 * num)
    $('.dan-' + (index + i)).remove();
    if (index === bulletChat.length - 1) {
      bulletChatTime = 0;
      bulletChatStart(index + i + 2);
    }
  });
}
// 关闭弹幕 主要是用于离开弹幕页时清空定时器等 防止污染及性能占用
function bulletChatStop() {
  timers.forEach(timer => {
    clearTimeout(timer)
  });
  timers = [];
  bulletChatTime = 0;
  $('.dan').remove();
}
// 调用开启弹幕方法
bulletChatStart()

总结

最近对移动端h5页面进行开发时,也算是回忆了一些关于css动画的知识,css还是蛮强大的,css能解决的问题,尽量就不要交给js了。而在写这个弹幕时候,也确实感受到了自己js和算法的知识还有待提高,总之,学无止境吧。

补充

解决了部分兼容问题:h5弹幕动画——解决部分手机兼容问题

;