NUTUI-React 弹幕组件的实现过程

541 阅读1分钟

NutUI 是一款京东风格的移动端组件库。NutUI 目前支持 Vue 和 React技术栈,支持Taro多端适配。

Barrage 组件用于话语和词组的轮播展示,适用于视频中或其他类似需求中。本文主要介绍 Barrage 组件的实现思路。

组件的功能

先来看一下 Barrage 组件需要实现哪些功能:

  • 多轨道弹幕
  • 弹幕在轨道上不重叠
  • 弹幕匀速滚动
  • 是否循环播放

基于这些功能点,就可以着手实现组件了。

组件的实现

根据功能点为组件设定 props,list 弹幕列表、rows 轨道行数、gapY 轨道的垂直间距、loop 是否循环、interval 弹幕出现的时间间隔、duration 弹幕的滚动时长。

构建弹幕容器 DOM 结构

<div ref={barrageContainer} />

获取弹幕容器宽度,确定单条弹幕滚动距离

在浏览器渲染后获取弹幕容器宽度,设置 css 变量

barrageCWidth.current = barrageContainer.current.offsetWidth
barrageContainer.current?.style.setProperty(
    '--move-distance',
    `-${barrageCWidth.current}px`
)

根据 --move-distance 变量,设置滚动动画,单条弹幕滚动距离,从元素左侧未进入容器右侧边缘起始,元素右侧越过容器左侧边缘截止

avatar

.move{
    animation-name: moving;
    animation-timing-function: linear;
}
@keyframes moving {
    from {
      transform: translateX(100%);
    }

    to {
      transform: translateX(var(--move-distance));
    }
}

弹幕在轨道上匀速滚动且不重叠

设置当前索引为0

index.current = 0

根据 rows 设置轨道行数,弹幕依次推入不同轨道。创建单条弹幕 DOM 元素,追加到轨道容器中, 获取元素宽度,结合 duration 计算当前弹幕自身从左到右滚动时长,从而获取当前弹幕滚动总时长,设置弹幕的 animationDuration 时长,至此单条弹幕就滚动起来了,当弹幕运动出容器外,需要从轨道中移除当前元素。

const run = () => {
    const _index = loop ? index.current % list.length : index.current
    let currentIndex = _index % rows

    const el = document.createElement(`div`)
    el.innerHTML = list[_index] as string
    el.classList.add('barrage-item')
    (barrageContainer.current as HTMLDivElement).appendChild(el)

    const width = el.offsetWidth
    const height = el.offsetHeight
    el.classList.add('move')
    const elScrollDuration = Math.ceil(
      (width / barrageCWidth.current) * duration
    )
    times.current[currentIndex] = elScrollDuration
    el.style.animationDuration = `${duration + elScrollDuration}ms`
    el.style.top = `${currentIndex * (height + gapY) + 20}px`
    el.style.width = `${width}px`

    el.addEventListener('animationend', () => {
      ;(barrageContainer.current as HTMLDivElement).removeChild(el)
    })
    index.current++
    run()
}

弹幕需要依次推入轨道, 当 loop 为 true 时,list 循环结束从头重新开始推入。
根据 interval 设置弹幕出现的时间间隔,弹幕滚动总时长由弹幕自身从左到右滚动时长和在弹幕容器滚动时长组成,根据当前轨道上一个弹幕的滚动时长,计算当前弹幕的推送时间间隔。

const run = () => {
    clearInterval(timer.current)
    let intervalCache = interval
    const _index = (loop ? index.current % list.length : index.current) % rows
    const result = times.current[_index] - rows * interval
    if (result > 0) {
        intervalCache += result
    }
    timer.current = window.setTimeout(() => {
        play()
    }, intervalCache)
}

最后

感谢阅读,文中可能存在不准确或错误之处,如果您发现任何问题,欢迎指正,非常感激。

NutUI GitHub 仓库:

NutUI 官网:nutui.jd.com

欢迎关注,欢迎 Star~