DEV Community

protium
protium

Posted on • Edited on

Javascript: html2canvas with videos

If you ever used html2canvas to get a "screenshot" of your web app you probably know that html2canvas doesn't work with videos yet, it renders a blank square. They are working in this feature (I think). So lets write a workaround in the meantime.

Before calling html2canvas method we need to take a capture of each video. By reading some source code and issues in the github repository I discovered that html2canvas renders all the images it finds, so if we have an image as background of your video it will render it in the final canvas.



let canvas = document.getElementById('canvas') // declare a canvas element in your html
let ctx = canvas.getContext('2d');
let videos = document.querySelectorAll('video')
let w, h
for (let i = 0, len = videos.length; i < len; i++) {
    const v = videos[i]
    if (!v.src) continue // no video here
    try {
        w = v.videoWidth
        h = v.videoHeight
        canvas.width = w
        canvas.height = h
        ctx.fillRect(0, 0, w, h)
        ctx.drawImage(v, 0, 0, w, h)
        v.style.backgroundImage = `url(${canvas.toDataURL()})` // here is the magic
        v.style.backgroundSize = 'cover' 
        ctx.clearRect(0, 0, w, h); // clean the canvas
    } catch (e) {
        continue
    }
}


Enter fullscreen mode Exit fullscreen mode

One problem we can see so far is the backgroundSize style. It may not be the same size as your video so you should do some adjustment.

Next use html2canvas as usual



html2canvas(document.body)
    .then((canvas) => {
        // display, save as jpeg
     })


Enter fullscreen mode Exit fullscreen mode

Tested with the latest version v1.0.0-alpha.12

Here a working example
Edit n9wrv84jlj

Why do I need to take screenshot of my web app?

Well there may be many reasons but my first thought would be user privacy violation!. But as I work in a project for a Digital Signage Company I learned that Screenshots are a mandatory feature. Users want to see what's being (or was) displayed in their remote displays anytime.

That's it, I hope someone in the cyberspace will find this useful
(my first post, yaaaai)

Top comments (10)

Collapse
 
rexn8r profile image
rexn8r

hi brian

excellent code. it works like a charm on windows desktop browser. however, the same code doesn't work in android.. i have an app developed in cordova using html/javascript/css and it doesn't capture the video element current frame. have you come across this issue or have any solution or pointer?

thanks
Rex

Collapse
 
protium profile image
protium

Hi, thanks!
I wrote this code 'cause I had the same issue with a cordova app (android 6.4) and it's working. Maybe you have some error, have you checked the logcat or console in web inspector?

Collapse
 
rexn8r profile image
rexn8r

Hi Brian,

Thanks for your response. I am also developing a digital signage system and using vidoe.js (videojs.com/) as the video player framework along with html2canvas javascript for capture. In the CMS preview (in desktop web browser) your code implementation works like a charm.

I am using the same set of code in cordova player app where the code fails. i.e. it gives black screen in place of video element. I checked the console in web inspector in visual studio cordova project and it doesn't produce any error. The code doesn't work in android emulator as well as on my tablet having android 4.4. could android 4.4 be the problem? does this code work only android 6.4 or higher only?

By the way I am also using Electron framework to build windows desktop player app with same code and in there, your code works absolutely fine.

so is the android version a problem???

thanks
rex

Thread Thread
 
protium profile image
protium

Hi! I meant the cordova android version 6.4.

I have this code working in Android 4.2 onward. But I've tested this on android devices that are not phones or tablets and they have full HD resolution. Maybe you can set a breakpoint and verify that the video background image is being generated before calling html2canvas. And also you should try the last release of html2canvas.

Thread Thread
 
rexn8r profile image
rexn8r

hi brian,

thanks for your response.

I am running cordova 6.3.1 with android 5.2.1 in visual studio 2017.

the video background image is being generated but its black. below is the code before calling html2canvas;

+++++++++++++++++++++
//take screenshot
var canvas = document.getElementById("canvas"); // declare a canvas element in your html
var ctx = canvas.getContext("2d");
var videos = document.querySelectorAll("video");
var w, h;
for (var i = 0, len = videos.length; i < len; i++) {
var v = videos[i];
if (!v.src) continue; // no video here
try {
w = v.videoWidth;
h = v.videoHeight;
canvas.width = w;
canvas.height = h;
ctx.fillRect(0, 0, w, h);
ctx.drawImage(v, 0, 0, w, h);
var a = canvas.toDataURL();
//document.getElementById("img1").src = a; -->> generates black image
v.style.backgroundImage = 'url(' + a + ')';
v.style.backgroundSize = "cover";

                ctx.clearRect(0, 0, w, h); // clean the canvas
            } catch (e) {
                console.log(e);
                continue;
            }
        }
Enter fullscreen mode Exit fullscreen mode

+++++++++++++++++++++++++++

i thought the video.js framework could be a problem so i tried following code to get the actual HTMLVideoElement from it but it even generates transparent image;


//$('.video-js').each(function () {
// videojs(this.id).ready(function () {
// var myPlayer = this.tech({ IWillNotUseThisInPlugins: true }).el();
// var w, h;
// try {
// w = myPlayer.videoWidth;
// h = myPlayer.videoHeight;
// canvas.width = w;
// canvas.height = h;
// ctx.fillRect(0, 0, w, h);
// ctx.drawImage(myPlayer, 0, 0, w, h);
// var a = canvas.toDataURL();
// document.getElementById("img1").src = a;
// myPlayer.style.backgroundImage = 'url(' + a + ')';
// myPlayer.style.backgroundSize = "cover";

        //            ctx.clearRect(0, 0, w, h); // clean the canvas
        //        } catch (e) {
        //            console.log(e);
        //            continue;
        //        }
        //    });
        //});
Enter fullscreen mode Exit fullscreen mode

so yes you are right, the above code not generating image from video element using drawImage function...

any pointer???

thanks
rex

Thread Thread
 
protium profile image
protium

Hi Rex. I had no idea but there are many factors to check:

  • video encoding
  • the video is loaded locally?
  • trying with another video
  • trying without videoJS
  • check if video metadata is loaded correctly (height, width)

I hope you find what's is going on an we can update this post.

Regards

Thread Thread
 
rexn8r profile image
rexn8r

Tried different video files, video files are stored in local sdcard and rendered from there. metadata are loaded, can see correct width and height of the video. can see HTMLVideoElement in the web console, tried without videoJS (i have been using videoJS cause it allows to play video without touch interaction). No luck whatsoever.

are you using plain video element in your code? if so how do u auto play the element? or you play the video with control and then capture it?

thanks
rex

Collapse
 
marcusx2 profile image
Marcus • Edited

This does not work on mobile Safari. Only windows and Android. Any workaround for Safari? I get the error Maximum call stack size exceeded

Collapse
 
cplusplusequalscplusone profile image
c-plus-plus-equals-c-plus-one

Hi Marcus, it's probably not related to the code of the post author, but probably it's related to your other code used. Can you share it with me?

Collapse
 
cplusplusequalscplusone profile image
c-plus-plus-equals-c-plus-one

Thank you so much, I have a lot of experience in Javascript, but seeing the if (condition) continue the first time ever! Interesting