PHP只负责弹幕内容存储与分发,不参与前端渲染;其核心职责是校验、存入MySQL/Redis、通过WebSocket广播,并严格做敏感词过滤与长度截断,高频写入需异步队列,避免直连数据库。

PHP 不负责弹幕渲染,只管存和推
弹幕动画、飞入轨迹、碰撞检测、透明度渐变——这些全是前端的事。PHP 做的只是把用户发的 content、time、color、user_id 存进 MySQL 或 Redis,并通过 WebSocket(如 Swoole)广播出去。
常见错误是试图用 PHP 输出 HTML 标签或写 CSS 动画,结果页面卡死、内存暴涨。真实生产环境里,/send 接口只做校验(长度、敏感词、频率限制),/pull 接口按时间范围分页查,不返回全量数据。
- 敏感词过滤必须在 PHP 层做:
str_replace()或正则替换,不能依赖前端 - 内容截断要严格:MySQL 字段设为
VARCHAR(30),插入前用mb_substr($danmaku, 0, 30, 'UTF-8') - 高频写入场景别直连 MySQL:先
lpush到 Redis 队列,再由后台 worker 异步INSERT
前端渲染上千条弹幕不掉帧的关键动作
每来一条就 document.createElement('div') + appendChild,500 条后必然掉帧。真正有效的做法是池化复用 + requestAnimationFrame 控制节奏。
核心不是“怎么加”,而是“怎么回收”。示例逻辑:
立即学习“PHP免费学习笔记(深入)”;
const pool = [];
for (let i = 0; i < 100; i++) {
const el = document.createElement('div');
el.className = 'danmaku';
el.style.cssText = 'position: absolute; left: 100%; opacity: 0;';
pool.push(el);
}
<p>function spawnDanmaku(text) {
const el = pool.pop() || document.createElement('div');
el.textContent = text;
el.style.left = '100%';
el.style.opacity = '0';
videoContainer.appendChild(el);</p><p>// 使用 rAF 启动动画,避免 layout thrashing
requestAnimationFrame(() => {
el.style.transition = 'left 8s linear, opacity 0.3s';
el.style.left = '-20%';
el.style.opacity = '1';
});</p><p>// 动画结束回收
setTimeout(() => {
el.remove();
pool.push(el);
}, 8300);
}注意:CSS 动画必须用 transform: translateX() 替代 left,否则触发重排;requestAnimationFrame 不是万能药,它只保证执行时机,不解决 DOM 节点爆炸问题。
视频合成必须用 ffmpeg,PHP 只调度不计算
PHP 本身不处理像素、帧率、编码器协商。它只是拼命令、起进程、等结果。直接调 exec() 风险高,推荐用 symfony/process 或 php-ffmpeg 封装。
常见坑点:
-
disable_functions里禁了proc_open?必须删掉,否则php-ffmpeg直接报错 - 路径别写相对路径:
'ffmpeg.binaries' => '/usr/local/bin/ffmpeg',绝对路径才可靠 - 合并不同编码的视频(比如 H.264 + VP9),
concat()会失败,得先统一转码再拼接 - 裁剪时用
-ss放前面(快)还是后面(准)?精确剪辑选后者,但慢;批量预处理选前者
示例裁剪命令(精准):ffmpeg -i input.mp4 -ss 00:01:20 -to 00:01:30 -c:v libx264 -c:a aac output.mp4
互动视频里弹幕和合成内容如何对齐时间轴
用户发弹幕时记录的是播放器当前 currentTime,但视频合成后原始时间戳会偏移。如果用剪辑后的视频做弹幕载体,历史弹幕全错位。
解决方案只有两个:
- 所有弹幕绑定「原始未剪辑视频」的时间戳,前端播放剪辑版时,按比例换算:比如原片 600 秒,剪成 120 秒,那弹幕时间
time * 0.2 - 合成时保留原始时间元数据(如用
ffmetadata写入章节信息),PHP 读取后动态映射,但兼容性差,浏览器支持弱
最稳妥的做法是:弹幕系统永远以原始视频为锚点,合成仅用于生成新资源链接,不改变已有弹幕逻辑。这点容易被忽略——上线后才发现弹幕全飘在黑屏上。



















