在尝试写一段弹幕组件,代码如下:
import { onMounted, ref, nextTick } from 'vue' import io from 'socket.io-client' export default { setup () { let socket const barrages = ref([]) // 存放弹幕数据 const winWidth = ref(window.innerWidth) // 获得屏幕宽度 const barrageEls = ref([]) // 获取每条弹幕 onMounted(() => { socket = io('http://localhost:3002') socket.on('connect', () => { console.log(socket.id, '监听客户端连接成功-connect') }) socket.on('to-client', async (data) => { // 计算最新一条弹幕的文字宽度,以便记录划出距离 const canvas = document.createElement('canvas') const context = canvas.getContext('2d') context.font = '12pt Avenir' const { width } = context.measureText(data.name + data.discourse) // 追加弹幕到 barrages 中 barrages.value[barrages.value.length] = { msg: data.name + ':' + data.discourse, speed: Math.round(Math.random() * 3), // 随机速度 top: Math.round(Math.random() * 100) } await nextTick() // 弹幕画出动画 const timer = setInterval(function () { // 获得新添加的弹幕 [barrageEls.value.length - 1] 并对其位置进行移动 if (barrageEls.value[barrageEls.value.length - 1].offsetLeft < -(Math.ceil(width))) { // 停止动画本质是停止定时器 clearInterval(timer) } barrageEls.value[barrageEls.value.length - 1].style.left = winWidth.value-- }, Math.round(Math.random() * 3)) }) }) return { barrages, barrageEls, winWidth } } }
但实际运行起来发现有如下 bug :
- 如果相近时间内提交两条弹幕,会导致速度叠加;
- 每条弹幕的左坐标位置并没有随弹幕内容变化;
- 在第一条弹幕移动时间内可以进行重复提交,但
clearInterval(timer)
后无法触发新弹幕滚动
目前还没有想到解决办法,还望可以得到提示,谢谢!
依照一楼的方案修改代码,解决了问题,但是又暴露了新的问题:
新的一条弹幕总会在视图上清楚上一条弹幕动画,我在想是否是因为 [barrageEls.value.length - 1]
的问题导致的,我尝试如下修改:
// 在新弹幕来的时候遍历对象判断该结束哪个动画 barrages.value.forEach(item => { if (item.left < -item.width) { clearInterval(item.timer) } }) …… barrages.value.push({ width: width, // 保存该弹幕宽度 msg: data.name + ':' + data.discourse, speed: Math.round(Math.random() * 3), top: Math.round(Math.random() * 100), left: winWidth.value, timer: null // 保存该弹幕动画 }) …… const timer = setInterval(function () { // 将终止动画函数放到了外面 barrages.value[barrages.value.length - 1].left-- barrageEls.value[barrageEls.value.length - 1].style.left = barrages.value[barrages.value.length - 1].left + 'px' }, Math.round(Math.random() * (10 - 5) + 5)) barrages.value[barrages.value.length - 1].timer = timer // 存储定时器
但是依旧会清除前面的弹幕 ???
又修改了一版,解决了新弹幕清楚旧弹幕的问题,但是此写法无法终止定时器,代码如下:
<template v-for="(item, index) in barrages" :key="index"> <div :style="{ top: item.top + '%', left: item.maxW + 'px' }"> {{ item.timer(item) }} // 运行当前对象中的定时器函数 {{ item.msg }} </div> </template> // 追加弹幕到 barrages 中 let newBarrage = { msg: data.name + ':' + data.discourse, top: Math.round(Math.random() * 100), speed: Math.round(Math.random() * (10 - 5) + 5), maxW: maxW, // maxW 屏幕总宽度 minW: width, // width 当前弹幕占用宽度 timer: (item) => { timer(item) } } function timer (item) { let timer = setInterval(() => { if (item.maxW < -item.minW) { clearInterval(timer) // 该方案此处无法终止定时器 } item.maxW-- }, item.speed) } barrages.value.push(newBarrage)
仍在想办法 Ing ……
import { onMounted, ref, nextTick } from 'vue' import io from 'socket.io-client' export default { setup () { let socket const barrages = ref([]) // 存放弹幕数据 const winWidth = ref(window.innerWidth) // 获得屏幕宽度 onMounted(() => { socket = io('http://localhost:3002') socket.on('connect', () => { console.log(socket.id, '监听客户端连接成功-connect') }) socket.on('to-client', async (data) => { // 计算最新一条弹幕的文字宽度,以便记录划出距离 const canvas = document.createElement('canvas') const context = canvas.getContext('2d') context.font = '12pt Avenir' const { width } = context.measureText(data.name + data.discourse) // 追加弹幕到 barrages 中 const newBarrage = { msg: data.name + ':' + data.discourse, speed: Math.round(Math.random() * 3), // 随机速度 top: Math.round(Math.random() * 100), left: winWidth.value, width: width, timer: null } barrages.value.push(newBarrage) await nextTick() // 弹幕画出动画 newBarrage.timer = setInterval(function () { // 获得新添加的弹幕 [barrageEls.value.length - 1] 并对其位置进行移动 if (newBarrage.left < -newBarrage.width) { // 停止动画本质是停止定时器 clearInterval(newBarrage.timer) } newBarrage.left-- }, newBarrage.speed) }) }) return { barrages, winWidth } } }