如何将带视频与文字的FabricJS画布导出为带声音的MP4?
解决方案:FabricJS画布导出为带音频的MP4
嘿,我懂你现在的痛点——FabricJS本身没提供直接导出视频的功能,但我们可以用浏览器的MediaRecorder API结合Canvas捕获能力,实现你要的「原视频背景+叠加文字+保留原音频」的MP4导出效果。下面是具体的实现步骤和代码:
核心思路
- 借助FabricJS的实时渲染,把画布内容持续绘制到可捕获的Canvas流中
- 提取原视频的音频轨道,和Canvas的视频流合并成一个完整的
MediaStream - 用
MediaRecorder录制这个合并后的流,最终导出为MP4文件
修改后的完整代码
<!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.2.3/fabric.min.js"></script> </head> <body> <button id="play">play</button> <button id="exportvideo">Export as video</button> <canvas id="c" width="480" height="360"></canvas> <video crossorigin="anonymous" id="video1" style="display: none" class="canvas-img" width="480" height="360"> <source id="video_src1" src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4"> </video> <script> $(document).ready(function() { let canvas = new fabric.Canvas('c'); canvas.setWidth(480); canvas.setHeight(360); let video1El = document.getElementById('video1'); let video1 = new fabric.Image(video1El, { left: 0, top: 0 }); canvas.add(video1); video1El.load(); // 保留原有播放按钮逻辑 $(document.body).on('click', '#play', function(){ let iText = new fabric.IText("sample text", { fill: 'red', fontSize: 60, left: 10, top: 10 }); canvas.add(iText); video1El.play(); }); // 导出视频核心逻辑 $(document.body).on('click', '#exportvideo', async function(){ // 先检查视频是否在播放 if (video1El.paused) { alert("请先点击播放按钮启动视频!"); return; } // 1. 捕获FabricJS画布的视频流(30帧/秒,可按需调整) const canvasStream = canvas.captureStream(30); // 2. 提取原视频的音频轨道并合并到视频流 const audioTrack = video1El.captureStream().getAudioTracks()[0]; if (audioTrack) { canvasStream.addTrack(audioTrack); } // 3. 配置MediaRecorder为MP4格式 const recordOptions = { mimeType: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' }; let mediaRecorder = new MediaRecorder(canvasStream, recordOptions); let recordedChunks = []; // 4. 收集录制的视频数据 mediaRecorder.ondataavailable = function(e) { if (e.data.size > 0) { recordedChunks.push(e.data); } }; // 5. 录制结束后生成下载链接 mediaRecorder.onstop = function() { const videoBlob = new Blob(recordedChunks, { type: 'video/mp4' }); const downloadUrl = URL.createObjectURL(videoBlob); const downloadLink = document.createElement('a'); downloadLink.href = downloadUrl; downloadLink.download = 'fabric-canvas-export.mp4'; downloadLink.click(); URL.revokeObjectURL(downloadUrl); // 释放浏览器资源 }; // 6. 启动录制(示例录制5秒,可改成监听视频结束事件) mediaRecorder.start(); alert("开始录制,5秒后自动下载!"); setTimeout(() => { mediaRecorder.stop(); }, 5000); // 可选:监听视频自然结束,自动停止录制 // video1El.onended = function() { // mediaRecorder.stop(); // }; }); // 保持FabricJS的实时渲染循环 fabric.util.requestAnimFrame(function render() { canvas.renderAll(); fabric.util.requestAnimFrame(render); }); }); </script> </body> </html>
关键细节说明
- 跨域处理:你的视频标签已经加了
crossorigin="anonymous",这非常关键!如果没有这个设置,浏览器会因为跨域限制阻止捕获视频流,确保视频服务器允许跨域访问。 - 帧率调整:
canvas.captureStream(30)中的30是录制帧率,可根据性能需求调整(比如24帧更接近电影播放效果)。 - 音频合并:通过
video1El.captureStream().getAudioTracks()提取原视频的音频轨道,合并到Canvas流中,这样导出的视频就能保留原声音。 - 录制时长:示例用
setTimeout设置了5秒录制,你可以替换成监听video1El.onended事件,让录制在视频自然播放结束后自动停止。
注意事项
- 浏览器兼容性:
MediaRecorderAPI在Chrome、Firefox上支持良好,Safari需要14.1以上版本,部分编码格式可能需要根据浏览器调整。 - 性能优化:如果画布内容复杂(比如多个动画图层),高帧率录制可能会卡顿,可适当降低帧率或简化画布内容。
- 编码格式:示例中的
mimeType是通用的MP4编码配置,如果出现录制失败,可以尝试调整编码参数适配目标浏览器。
内容的提问来源于stack exchange,提问作者Umesh Patadiya
相关产品推荐
相关产品推荐

