HTML5 Web Worker 不能操作 DOM 或 Canvas,弹幕碰撞检测与重叠计算须在 Worker 中纯逻辑完成,渲染交由主线程;Worker 负责计算“显示内容、位置、偏移”,主线程负责绘制。

HTML5 Web Worker 本身不能直接操作 DOM 或视频元素,也无法访问 Canvas 的渲染上下文,因此弹幕的碰撞检测与重叠计算必须在 Worker 中纯逻辑完成,而渲染(绘制、定位、动画)必须交还主线程。关键在于:Worker 负责“算什么弹幕该显示、在哪显示、是否需偏移”,主线程负责“把结果画出来”。下面分步说明如何实现。
1. 弹幕数据结构与坐标抽象化
Worker 无法获取屏幕像素或 DOM 尺寸,所以必须由主线程预先传入必要上下文参数,并将弹幕位置抽象为归一化坐标或相对行高单位:
- 主线程计算并传入:
{ videoWidth, videoHeight, danmakuHeight, maxLines, safeLeft, safeRight } - 每条弹幕携带基础属性:
{ id, text, duration, speed, fontSize, color },不包含绝对坐标 - Worker 内部用“行号 + 水平进度”模拟位置:例如
line: 2, progress: 0.75表示第 2 行、从右向左运动到 75% 处
2. 时间驱动的帧级调度与碰撞建模
Worker 不应逐像素检测,而是按时间切片(如每 60ms)推进所有活动弹幕,并维护一个“当前帧活跃弹幕表”,按行分组进行区间重叠判断:
- 为每行维护一个
Map<id, { left, right, timestamp }>,记录该行中每个弹幕在当前时刻的左右边界(单位:归一化横坐标) - 对同一行内所有弹幕,用区间重叠算法快速判断:
!(a.right < b.left || b.right < a.left) - 发现重叠时,自动为后进入的弹幕分配下一行(
line++),若超出maxLines则丢弃或缓存等待空位 - 使用时间戳标记每条弹幕的“入场时间”,结合
speed和duration推算任意时刻的left/right
3. 主线程与 Worker 的高效通信协议
避免高频传输原始文本或样式,只同步轻量状态变更:
立即学习“前端免费学习笔记(深入)”;
- 主线程发送:
{ type: 'init', config }(初始化)、{ type: 'add', danmaku }(新增弹幕) - Worker 回传:
{ type: 'layout', items: [{ id, line, startProgress, endProgress, duration }] }—— 只含布局决策,不含渲染指令 - 使用
transferable传递 typed array(如Int32Array存储批量弹幕 ID 和行号),减少序列化开销 - 对高频弹幕流,启用
postMessage(..., [arrayBuffer])配合双缓冲队列,避免主线程卡顿
4. 主线程渲染层的精准对齐策略
Worker 输出的是逻辑行号和归一化进度,主线程需将其映射为真实 CSS/Canvas 坐标:
- 根据
videoHeight和danmakuHeight计算每行 top 偏移:top = line * danmakuHeight + baselineOffset - 水平位置由
progress插值得到:left = videoWidth * (1 - progress) - textWidth / 2(居中对齐) - 使用
requestAnimationFrame驱动动画,每一帧读取 Worker 最新 layout 数据,仅更新 DOM style 或 Canvas 绘制位置 - 对已离开视口的弹幕,主线程主动调用
worker.postMessage({ type: 'remove', id })通知 Worker 清理内部状态
整个流程不依赖第三方库,核心是职责分离:Worker 做确定性计算,主线程做不可代理的渲染。只要控制好消息粒度和坐标抽象层级,即使每秒数百条弹幕,也能保持 60fps 流畅。



















