jbuiss0n jbuiss0n - 4 months ago 10
Javascript Question

Play part of a video in a canvas

I would like to play only half of my video on a canvas but truncated on the diagonal.
At the moment I can play my full video in a canvas easyli using this little javascript:

var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var drawFrame = function drawFrame() {
context.drawImage(video, 0, 0, canvas.width, canvas.height);


In the end, I want to be able to play 2 videos, at the same time (I just have to call drawImage on each video), but each will be cut on diagonal, to render something like this:

enter image description here

I then want to move the separator depending on the mouse position.


You can use composition to achieve this.

Compositions uses the alpha channel to combine non-alpha data in various way, for example "source-in" will paint source image/video where there exist pixels while "destination-over" will keep the existing pixels where there is non-alpha and only paint new ones where there is alpha (these are also known as Porter-Duff or alpha composition. Blending comes under the same umbrella but has a different purpose than compositing).

There are many modes but the above mentioned ones will allow us to combine them to get the part we want just by defining an alpha region that function as a mask for both. I'll include an overview to show how the operators work for each mode to better understand what they do (from Wikipedia):

Comp modes

The steps needed in this case would be:

  • Initialize with composition mode "source-over" as we use this in a loop
  • Clear canvas to make sure we have an alpha channel to work with
  • Draw diagonal half relative to the mouse position
  • Draw video source 2 on top using "source-in" (to place it at the left side)
  • Draw video source 1 on top using "destination-over"

Working Example

This will load two different videos (links borrowed from here) and when loaded sets up the loop as described above (just give the videos a few seconds to load):

var ctx = c.getContext("2d"), pos = c.width * 0.5, count = 2;
ctx.fillText("Please wait while videos are loading...", 20, 20);
video1.oncanplay = video2.oncanplay = function() {if (!--count) renderFrame()};

function renderFrame() {
  ctx.globalCompositeOperation = "source-over";
  ctx.clearRect(0, 0, c.width, c.height);     // makes sure we have an alpha channel

  ctx.beginPath();                            // draw diagonal half
  ctx.moveTo(0, 0);
  ctx.lineTo(pos - 50, 0);
  ctx.lineTo(pos + 50, c.height);
  ctx.lineTo(0, c.height);

  // video source 2
  ctx.globalCompositeOperation = "source-in";        // comp in source 2
  ctx.drawImage(video2, 0, 0, c.width, c.height);
  // video source 1
  ctx.globalCompositeOperation = "destination-atop"; // comp in source 1
  ctx.drawImage(video1, 0, 0, c.width, c.height);


c.onmousemove = function(e) {
  pos = e.clientX - c.getBoundingClientRect().left;
video {display:none}
<canvas id=c width=640 height=400></canvas>
<video id=video1 muted autoplay loop src="http://media.w3.org/2010/05/sintel/trailer.mp4"></video>
<video id=video2 muted autoplay loop src="http://media.w3.org/2010/05/video/movie_300.webm"></video>