弹幕在视频播放时怎么做到不重叠还流畅滚动?

公孙熙妍 阅读 30

我正在做一个带弹幕功能的视频播放页,现在弹幕是用绝对定位从右往左移动的。但弹道(就是同一时间出现的弹幕行)经常互相覆盖,尤其是高密度的时候,看着特别乱。

我试过给每条弹幕动态计算 top 值避开已有弹幕,但性能很差,一卡一卡的。有没有什么高效又不重叠的实现思路?比如是不是该用 Canvas 或者 CSS 动画优化?

现在的关键代码大概是这样:

function createDanmaku(text) {
  const el = document.createElement('div');
  el.textContent = text;
  el.style.position = 'absolute';
  el.style.top = findAvailableTrack() + 'px'; // 这个函数遍历现有弹幕找空位
  el.style.left = '100%';
  container.appendChild(el);
  // 然后用 transition 实现移动
  setTimeout(() => el.style.transform = <code>translateX(-${container.offsetWidth}px)</code>, 0);
}
我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
志信酱~
问题在于逐条计算弹幕位置性能太差。可以用 canvas 绘制弹幕,按行管理弹幕队列,避免重叠。这样性能更好,视觉效果也更流畅。
function drawDanmaku(ctx, danmuList) {
danmuList.forEach(danmu => {
if (danmu.x > -danmu.width) {
ctx.fillText(danmu.text, danmu.x, danmu.y);
danmu.x -= danmu.speed;
}
});
}
点赞
2026-03-24 17:17
设计师皓轩
你这个问题我遇到过,DOM元素+CSS动画做弹幕确实容易卡。主要瓶颈在两个方面:一是频繁的DOM操作,二是findAvailableTrack这种实时计算太吃性能。

建议改用Canvas方案,性能能提升一个量级。核心思路是:
1. 预定义几条弹道(比如5-8条),固定y坐标
2. 每条弹道维护一个队列,记录当前是否有弹幕在占用
3. 新弹幕选择可用弹道,而不是实时计算位置

给你个简化版实现:

const tracks = [20, 50, 80]; // 预定义的弹道y坐标
const trackStatus = [false, false, false]; // 对应弹道占用状态

function createDanmaku(text) {
const ctx = canvas.getContext('2d');
const trackIndex = trackStatus.findIndex(used => !used);
if (trackIndex === -1) return; // 没找到可用弹道就丢弃

trackStatus[trackIndex] = true;
const y = tracks[trackIndex];
let x = canvas.width;

function animate() {
ctx.clearRect(x-1, y-20, text.length*10+2, 24);
ctx.fillText(text, x, y);
x -= 2;

if (x < -text.length*10) {
trackStatus[trackIndex] = false;
} else {
requestAnimationFrame(animate);
}
}
animate();
}


优化点:
1. 用requestAnimationFrame代替CSS动画,更流畅
2. 避免频繁DOM操作,Canvas只重绘变化部分
3. 弹道状态管理简单高效,不用实时计算

如果弹幕量特别大,还可以考虑WebGL方案。不过Canvas对大多数场景已经够用了,我去年用类似方案做到同时2000条弹幕不卡顿。
点赞 2
2026-03-09 16:34