如何将HTML Canvas与ffmpeg.wasm绑定生成视频(无需服务器)
将Canvas物理模拟帧编码为视频(基于ffmpeg.wasm)
完全可以通过ffmpeg.wasm实现将Canvas渲染的物理模拟帧转换为视频,核心流程是按固定帧率生成模拟帧 → 捕获Canvas帧数据写入ffmpeg虚拟文件系统 → 调用ffmpeg命令编码为视频。以下是具体实现方案,适配你提供的物理模拟器代码:
1. 初始化ffmpeg.wasm
先完成ffmpeg.wasm的基础加载(可通过npm或CDN引入):
import { createFFmpeg } from '@ffmpeg/ffmpeg'; // 初始化ffmpeg实例,开启日志便于调试 const ffmpeg = createFFmpeg({ log: true }); await ffmpeg.load();
2. 改造物理模拟器为离线帧生成模式
实时渲染的requestAnimationFrame依赖屏幕刷新率,无法保证视频帧率稳定,需要改成固定时间步进的离线渲染模式,按目标视频帧率生成每帧:
// 视频参数配置 const VIDEO_FPS = 30; const FRAME_TIME_MS = 1000 / VIDEO_FPS; // 单帧时长(毫秒) const TOTAL_FRAMES = 180; // 生成6秒视频(30fps × 6s) // 重置模拟器状态 let simulationTime = 0; const canvas = document.getElementById('your-canvas'); const ctx = canvas.getContext('2d'); let physicsObjects = [/* 你的物理对象实例数组 */]; // 生成单帧并写入ffmpeg虚拟文件 async function renderAndSaveFrame(frameIndex) { // 计算当前帧的时间增量 const dt = FRAME_TIME_MS / 1000; // 清空画布并更新渲染物理对象 ctx.clearRect(0, 0, canvas.width, canvas.height); for (const obj of physicsObjects) { obj.update(dt); obj.render(canvas, ctx); } // 将Canvas内容转为Uint8Array格式 const dataUrl = canvas.toDataURL('image/png'); const rawData = atob(dataUrl.split(',')[1]); const uint8Frame = Uint8Array.from(rawData, char => char.charCodeAt(0)); // 写入ffmpeg虚拟文件系统,命名格式适配ffmpeg序列帧解析 ffmpeg.FS('writeFile', `frame_${String(frameIndex).padStart(3, '0')}.png`, uint8Frame); simulationTime += FRAME_TIME_MS; } // 批量生成所有帧 async function generateAllFrames() { for (let i = 0; i < TOTAL_FRAMES; i++) { await renderAndSaveFrame(i); } }
3. 调用ffmpeg编码视频
帧生成完成后,执行ffmpeg命令将序列帧编码为MP4(或其他格式):
await generateAllFrames(); // 执行ffmpeg编码命令:序列帧转MP4,指定帧率、编码器 await ffmpeg.run( '-framerate', String(VIDEO_FPS), '-i', 'frame_%03d.png', // 匹配之前的帧命名格式 '-c:v', 'libx264', // H.264编码器 '-pix_fmt', 'yuv420p', // 兼容大部分播放器的像素格式 'physics_sim.mp4' ); // 导出并下载视频 const videoData = ffmpeg.FS('readFile', 'physics_sim.mp4'); const downloadUrl = URL.createObjectURL(new Blob([videoData.buffer], { type: 'video/mp4' })); const downloadLink = document.createElement('a'); downloadLink.href = downloadUrl; downloadLink.download = 'physics_simulation.mp4'; downloadLink.click(); // 清理ffmpeg虚拟文件系统 for (let i = 0; i < TOTAL_FRAMES; i++) { ffmpeg.FS('unlink', `frame_${String(i).padStart(3, '0')}.png`); } ffmpeg.FS('unlink', 'physics_sim.mp4');
关键优化与注意事项
- 帧率一致性:必须用固定
dt计算物理更新,避免实时渲染的帧率波动导致视频速度异常。 - 性能优化:如果生成大量帧,建议用BMP格式替代PNG(
canvas.toDataURL('image/bmp')),编码速度更快且内存占用更低。 - 分辨率适配:Canvas的
width和height决定视频分辨率,建议设置为标准尺寸(如1280×720),避免后期拉伸。 - ffmpeg内核配置:若出现编码错误,确保加载的ffmpeg内核包含所需编码器(如libx264),可在
createFFmpeg时指定corePath指向对应内核文件。
内容的提问来源于stack exchange,提问作者paladynee
相关产品推荐
相关产品推荐

