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:
- Play video and while it’s playing, listen for each frame.
- Render that frame to the canvas and wait for the next frame.
- 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).