34

i have written this function to capture each frame for the GIF but the output is very laggy and crashes when the data increases. Any suggestions ?

Code :

    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);
        }
Hashir Salam
  • 445
  • 1
  • 5
  • 7
  • see also [Capture HTML canvas as GIF/JPG/PNG/PDF?](https://stackoverflow.com/questions/923885/capture-html-canvas-as-gif-jpg-png-pdf) – milahu Mar 06 '23 at 09:39

2 Answers2

69

In modern browsers you can use a conjunction of the MediaRecorder API and the HTMLCanvasElement.captureStream method.

The MediaRecorder API will be able to encode a MediaStream in a video or audio media file on the fly, resulting in far less memory needed than when you grab still images.

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>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I think part of the point was that performance goes down as the number of Blobs increases, so storing them until you can execute this code might not be viable. – TheJim01 Jun 04 '18 at 16:37
  • 3
    @TheJim01 I probably should have explained that MediaRecorder encodes the video on the fly, so it will not save still images, and the `chunks` we store there are really chunks of the **final** media file i.e already encoded as webm, or said differently, not still images. You keep only the data of the final video in memory. – Kaiido Jun 05 '18 at 00:46
  • Hi, I'm trying to use this code but I got error like: canvas.captureStream is not a function – Umesh Patadiya Jan 21 '19 at 03:32
  • @UmeshPatadiya from which browser? Microsoft ones still don't support this method. – Kaiido Jan 21 '19 at 05:29
  • I'm using chrome on windows. My project is in angular 6. – Umesh Patadiya Jan 21 '19 at 05:52
  • @UmeshPatadiya ... I don't know angular, do they wrap everything in their own object in a TypeScript fashion? In this case, you'd probably have to extend whatever object you have so that it implements the native element's methods. And you could first check `console.log(canvas instanceof HTMLCanvasElement)` to be sure you are dealing with a canvas. – Kaiido Jan 21 '19 at 05:57
  • @Kaiido I'm getting false in above log, one more thing is I'm using fabricJS canvas. – Umesh Patadiya Jan 21 '19 at 06:07
  • Then what is `canvas` if it is not an HTMLCanvasElement? Do you set it to `canvas = new fabric.Canvas()`? In that case, this is not a canvas you have here, but a fabricjs object. You'd need to call its [`getElement`](https://fabricjs.com/docs/fabric.Canvas.html#getElement) method to retrieve the real canvas element. – Kaiido Jan 21 '19 at 06:12
  • Thanks @Kaiido It's working now. I added getElement(). now let me know once thing. Is it possible to record with the sound? – Umesh Patadiya Jan 21 '19 at 06:40
  • @UmeshPatadiya https://stackoverflow.com/questions/39302814/mediastream-capture-canvas-and-audio-simultaneously/39302994#39302994 – Kaiido Jan 21 '19 at 06:42
  • Does this approach support saving transparency / alpha into the webm (like I'm hoping for here https://stackoverflow.com/q/58190652/470749)? – Ryan Oct 01 '19 at 20:36
  • The recording I'm getting is so blurry / poor quality. Is there any way to fix that? – Kevin Wheeler Sep 04 '20 at 03:55
  • hi again @Kaiido. do you know if this architecture would hurt video quality? html5 canvas editor where users animate text, upload multiple video formats (.mp4, .mov, .avi, .wmv), and upload audio tracks; canvas exports webm; server uses ffmpeg to convert webm to multiple formats (.mp4, .mov, .avi, .wmv)? – Crashalot Jan 09 '21 at 21:14
  • How to save as gif? Could you add the code for that? Even if i change the type from "video/webm" to "image/gif" and change the file ending in the download i still get a webm video... – FireFuro99 Jul 10 '22 at 12:15
  • On Safari/Webkit, the stream must set to the mime-type "video/mp4" – dast Sep 02 '23 at 10:15
10

You can also use https://github.com/spite/ccapture.js/ to capture to gif or video.

SMUsamaShah
  • 7,677
  • 22
  • 88
  • 131