如何将画布动画保存为gif或webm格式?

36

我编写了这个函数来捕获GIF的每一帧,但输出非常卡顿,并且在数据增加时会崩溃。有什么建议吗?

代码:

    function createGifFromPng(list, framerate, fileName, gifScale) {
            gifshot.createGIF({
                'images': list,
                'gifWidth': wWidth * gifScale,
                'gifHeight': wHeight * gifScale,
                'interval': 1 / framerate,
            }, function(obj) {
                if (!obj.error) {
                    var image = obj.image;
                    var a = document.createElement('a');
                    document.body.append(a);
                    a.download = fileName;
                    a.href = image;
                    a.click();
                    a.remove();
                }
            });
        }
/////////////////////////////////////////////////////////////////////////

function getGifFromCanvas(renderer, sprite, fileName, gifScale, framesCount, framerate) {
            var listImgs = [];
            var saving = false;
            var interval = setInterval(function() {
                renderer.extract.canvas(sprite).toBlob(function(b) {
                    if (listImgs.length >= framesCount) {
                        clearInterval(interval);
                        if (!saving) {
                        createGifFromPng(listImgs, framerate, fileName,gifScale);
                            saving = true;
                        }
                    }
                    else {
                        listImgs.push(URL.createObjectURL(b));
                    }
                }, 'image/gif');
            }, 1000 / framerate);
        }

2个回答

70

在现代浏览器中,你可以同时使用MediaRecorder APIHTMLCanvasElement.captureStream方法。

使用MediaRecorder API,可以将一个MediaStream实时编码成视频或音频文件,这样所需的内存要比抓取静态图片时少得多。

const ctx = canvas.getContext('2d');
var x = 0;
anim();
startRecording();

function startRecording() {
  const chunks = []; // here we will store our recorded media chunks (Blobs)
  const stream = canvas.captureStream(); // grab our canvas MediaStream
  const rec = new MediaRecorder(stream); // init the recorder
  // every time the recorder has new data, we will store it in our array
  rec.ondataavailable = e => chunks.push(e.data);
  // only when the recorder stops, we construct a complete Blob from all the chunks
  rec.onstop = e => exportVid(new Blob(chunks, {type: 'video/webm'}));
  
  rec.start();
  setTimeout(()=>rec.stop(), 3000); // stop recording in 3s
}

function exportVid(blob) {
  const vid = document.createElement('video');
  vid.src = URL.createObjectURL(blob);
  vid.controls = true;
  document.body.appendChild(vid);
  const a = document.createElement('a');
  a.download = 'myvid.webm';
  a.href = vid.src;
  a.textContent = 'download the video';
  document.body.appendChild(a);
}

function anim(){
  x = (x + 1) % canvas.width;
  ctx.fillStyle = 'white';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle = 'black';
  ctx.fillRect(x - 20, 0, 40, 40);
  requestAnimationFrame(anim);
}
<canvas id="canvas"></canvas>


3
我会尽力进行翻译,以下是翻译的结果:@TheJim01,也许我应该解释一下MediaRecorder实时编码视频的情况,所以它不会保存静态图像,在这里我们存储的“块”实际上是最终媒体文件的块,即已经编码为webm格式的块,或者说,不是静态图像。您只需在内存中保留最终视频的数据。 - Kaiido
@UmeshPatadiya 是用哪个浏览器?微软浏览器仍不支持这种方法。 - Kaiido
我在Windows上使用Chrome浏览器。我的项目是基于Angular 6的。 - Umesh Patadiya
@UmeshPatadiya ... 我不懂Angular,他们是否以TypeScript的方式将所有内容都包装在自己的对象中?如果是这样,您可能需要扩展您拥有的任何对象,以便它实现本机元素的方法。您可以首先检查console.log(canvas instanceof HTMLCanvasElement)以确保您正在处理画布。 - Kaiido
@UmeshPatadiya https://dev59.com/hlkS5IYBdhLWcg3w6qMz#39302994 - Kaiido
显示剩余10条评论

10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接