Pulling Circles Out of Videos with Canvas

I’ll keep this short. Canvas will render pixel data as fast as a video can play (up to certain dimension 640×380 or around there). What’s more, once you have that pixel data rendered to a canvas element you can manipulate things pixel by pixel on another canvas tag.

It goes something like this:

  1. Play video and while it’s playing, listen for each frame.
  2. Render that frame to the canvas and wait for the next frame.
  3. Once you render to canvas, grab that pixel data, manipulate it and render that data to another canvas.

What it looks like is this:

DEMO

So all I’m doing here is grabbing video data, rendering to the first canvas. Then taking that pixel data and rendering to a second canvas. Then I’m taking a click event and rendering pixel data from your mouse origin to a third canvas that follows your mouse.

That’s it.

Here’s the code:

var App = {

    init: function() {

        // find the video
        this.video = document.getElementById('cVideo');

        // get canvas one
        this.c1 = document.getElementById('c1');
        this.ctx1 = this.c1.getContext('2d');

        // get canvas 2
        this.c2 = document.getElementById('c2');
        this.ctx2 = this.c2.getContext('2d');
        this.ctx2.fillStyle = 'white';

        // get mouse following canvas
        this.c3 = document.getElementById('c3');
        this.ctx3 = this.c3.getContext('2d');

        // define global so it works in the setTimout, setInterval and window
        // events
        var self = this;

        // set up a listener for the video when it's playing
        this.video.addEventListener('playing', function(evt) {

            self.width = self.video.videoWidth;
            self.height = self.video.videoHeight;
            self.callback();

        }, false);

        // loop the video (firefox)
        this.video.addEventListener('ended', function(evt) {
            self.video.play();
        });

        // listen for mouse down and show the third canvas
        this.video.addEventListener('mousedown', function(evt) {
            self.c3.style.display = 'block';
            self.clientX = evt.clientX;
            self.clientY = evt.clientY;
            self.clicked = true;
        }, false);

        // listen for mouse down and show the third canvas
        this.c2.addEventListener('mousedown', function(evt) {
            self.c3.style.display = 'block';
            self.clientX = evt.clientX;
            self.clientY = evt.clientY;
            self.clicked = true;
        }, false);

        // listen for mouse movement and track it for canvas 3
        window.addEventListener('mousemove', function(evt) {
            self.mouseX = evt.clientX;
            self.mouseY = evt.clientY;
        }, false);

        // listen for mouse release and hide canvas 3
        window.addEventListener('mouseup', function(evt) {
            self.c3.style.display = 'none';
        }, false);

    },

    // draw to canvases and call this function again as fast as you can
    callback: function() {
        this.computeFrame();
        var self = this;
        setTimeout(function() {
            self.callback();
        }, 0);

    },

    // draw video frame by frame on canvas elements
    computeFrame: function() {

        this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
        var frame = this.ctx1.getImageData(0, 0, this.width, this.height);

        var box = this.ctx1.getImageData(this.clientX - 50, this.clientY - 50, 100, 100);

        this.ctx2.putImageData(frame, 0, 0);

        if(this.clicked) {

            this.c2.style.position = 'absolute';
            this.c2.style.top = '0';
            this.c2.style.left = '0';

            this.ctx3.putImageData(box, 0, 0);
            this.c3.style.position = 'absolute';
            this.c3.style.top = (this.mouseY - 50) + 'px';
            this.c3.style.left = (this.mouseX - 50) + 'px';
            this.ctx2.beginPath();
            this.ctx2.arc(this.clientX, this.clientY, 50, 0, Math.PI*2, true);
            this.ctx2.fill();

        }

        return;
    }

}

Basically, we get the canvas and video elements. Set up some listeners. Then monitor video playback, and each frame of the video, render that to different canvas elements.

NOTE: works best in firefox, chrome is second, safari sucks and of course IE doesn’t work at all probably (I didn’t even test it).

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.