Related
is it possible to display a html5-video as part of the canvas?
basically the same way as you draw an Image in the canvas.
context.drawVideo(vid, 0, 0);
thanks!
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');
video.addEventListener('play', function () {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
I guess the above code is self Explanatory, If not drop a comment below, I will try to explain the above few lines of code
Edit :
here's an online example, just for you :)
Demo
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');
// set canvas size = video size when known
video.addEventListener('loadedmetadata', function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
});
video.addEventListener('play', function() {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
<div id="theater">
<video id="video" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" controls></video>
<canvas id="canvas"></canvas>
<label>
<br />Try to play me :)</label>
<br />
</div>
Here's a solution that uses more modern syntax and is less verbose than the ones already provided:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener("play", () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
Some useful links:
MDN Documentation for window.requestAnimationFrame()
Can I use requestAnimationFrame?
Using canvas to display Videos
Displaying a video is much the same as displaying an image. The minor differences are to do with onload events and the fact that you need to render the video every frame or you will only see one frame not the animated frames.
The demo below has some minor differences to the example. A mute function (under the video click mute/sound on to toggle sound) and some error checking to catch IE9+ and Edge if they don't have the correct drivers.
Keeping answers current.
The previous answers by user372551 is out of date (December 2010) and has a flaw in the rendering technique used. It uses the setTimeout and a rate of 33.333..ms which setTimeout will round down to 33ms this will cause the frames to be dropped every two seconds and may drop many more if the video frame rate is any higher than 30. Using setTimeout will also introduce video shearing created because setTimeout can not be synced to the display hardware.
There is currently no reliable method that can determine a videos frame rate unless you know the video frame rate in advance you should display it at the maximum display refresh rate possible on browsers. 60fps
The given top answer was for the time (6 years ago) the best solution as requestAnimationFrame was not widely supported (if at all) but requestAnimationFrame is now standard across the Major browsers and should be used instead of setTimeout to reduce or remove dropped frames, and to prevent shearing.
The example demo.
Loads a video and set it to loop. The video will not play until the you click on it. Clicking again will pause. There is a mute/sound on button under the video. The video is muted by default.
Note users of IE9+ and Edge. You may not be able to play the video format WebM as it needs additional drivers to play the videos. They can be found at tools.google.com Download IE9+ WebM support
// This code is from the example document on stackoverflow documentation. See HTML for link to the example.
// This code is almost identical to the example. Mute has been added and a media source. Also added some error handling in case the media load fails and a link to fix IE9+ and Edge support.
// Code by Blindman67.
// Original source has returns 404
// var mediaSource = "http://video.webmfiles.org/big-buck-bunny_trailer.webm";
// New source from wiki commons. Attribution in the leading credits.
var mediaSource = "http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"
var muted = true;
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info
var video = document.createElement("video"); // create a video element
video.src = mediaSource;
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
video.muted = muted;
videoContainer = { // we will add properties as needed
video : video,
ready : false,
};
// To handle errors. This is not part of the example at the moment. Just fixing for Edge that did not like the ogv format video
video.onerror = function(e){
document.body.removeChild(canvas);
document.body.innerHTML += "<h2>There is a problem loading the video</h2><br>";
document.body.innerHTML += "Users of IE9+ , the browser does not support WebM videos used by this demo";
document.body.innerHTML += "<br><a href='https://tools.google.com/dlpage/webmmf/'> Download IE9+ WebM support</a> from tools.google.com<br> this includes Edge and Windows 10";
}
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
function readyToPlayVideo(event){ // this is a referance to the video
// the video may not match the canvas size so find a scale to fit
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
// the video can be played so hand it off to the display function
requestAnimationFrame(updateCanvas);
// add instruction
document.getElementById("playPause").textContent = "Click video to play/pause.";
document.querySelector(".mute").textContent = "Mute";
}
function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height);
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
video.muted = muted;
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}
function drawPayIcon(){
ctx.fillStyle = "black"; // darken display
ctx.globalAlpha = 0.5;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#DDD"; // colour of play icon
ctx.globalAlpha = 0.75; // partly transparent
ctx.beginPath(); // create the path for the icon
var size = (canvas.height / 2) * 0.5; // the size of the icon
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1; // restore alpha
}
function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
function videoMute(){
muted = !muted;
if(muted){
document.querySelector(".mute").textContent = "Mute";
}else{
document.querySelector(".mute").textContent= "Sound on";
}
}
// register the event
canvas.addEventListener("click",playPauseClick);
document.querySelector(".mute").addEventListener("click",videoMute)
body {
font :14px arial;
text-align : center;
background : #36A;
}
h2 {
color : white;
}
canvas {
border : 10px white solid;
cursor : pointer;
}
a {
color : #F93;
}
.mute {
cursor : pointer;
display: initial;
}
<h2>Basic Video & canvas example</h2>
<p>Code example from Stackoverflow Documentation HTML5-Canvas<br>
Basic loading and playing a video on the canvas</p>
<canvas id="myCanvas" width = "532" height ="300" ></canvas><br>
<h3><div id = "playPause">Loading content.</div></h3>
<div class="mute"></div><br>
<div style="font-size:small">Attribution in the leading credits.</div><br>
Canvas extras
Using the canvas to render video gives you additional options in regard to displaying and mixing in fx. The following image shows some of the FX you can get using the canvas. Using the 2D API gives a huge range of creative possibilities.
Image relating to answer Fade canvas video from greyscale to color
See video title in above demo for attribution of content in above inmage.
You need to update currentTime video element and then draw the frame in canvas. Don't init play() event on the video.
You can also use for ex. this plugin https://github.com/tstabla/stVideo
I started with the answer from 2018 about requestAnimationFrame. However, it has three problems.
unnecessarily complex
there is a new way to handle this task (since about 2020)
attaching an event listener to the play event invites performance issues
First, the event listener can simply be connected to the function that does your desired processing and schedules the next call to itself. There's no need to wrap it in an anonymous function with another call to requestAnimationFrame.
Second, don't use requestAnimationFrame. That function schedules the next call to your callback as quickly as the browser can handle (generally 60Hz), which results in a significant processing workload. video.requestVideoFrameCallback only calls your callback when the video proceeds to its next frame. This reduces the workload when a video runs at less than 60 FPS and obviates the need for any processing at all while the video isn't playing, significantly improving performance.
Third, an event listener attached to the play event will fire whenever you tell the video to video.play(), which you'll do every time you load a new video into the video element and tell it to start playing, and also when you resume playback after using video.pause(). Thus, the video is drawn on the canvas (plus whatever other processing you're doing) once for each time the video has been told to play(), which quickly accumulates.
If you're sure you'll only be playing one video which you'd like to pause and resume, you can toggle play/pause by changing the playback rate, e.g. video.playbackRate = !video.playbackRate. If you'll be loading multiple videos into this element, it's better to forego the play event listener entirely and insert a manual call to step() when you load your first video to get that started. Note that this is active on the video element, not on specific loaded videos, so you'll need to either set and check a flag to ensure that you only call step() when loading the first video, or cancel any active video frame request before making a new one (shown below).
let animation_handle;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener('loadeddata', video_load_callback, false);
function video_load_callback() {
video.cancelVideoFrameCallback(animation_handle);
step()
}
function step() { // update the canvas when a video proceeds to next frame
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
animation_handle = video.requestVideoFrameCallback(step);
}
I'm trying to build a very simple animation function. I'm using this tutorial to build my project:
https://www.youtube.com/watch?v=hUCT4b4wa-8
The result after the button is clicked should be a green box moving across the page from left to right. When the button is clicked, nothing happens and I don't get any console errors.
Here's my fiddle:
https://jsfiddle.net/xkhpmrtu/7/
And here's a snippet of my code:
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border: 1px solid #666;
}
</style>
<script type="application/javascript" language="javascript">
function anim(x,y) {
var canvas = document.getElementById('canvas');//reference to canvas element on page
var ctx = canvas.getContext('2d');//establish a 2d context for the canvas element
ctx.save();//save canvas state if required (not required for the tutoriral anaimation, but doesn't hurt the script so it stays for now)
ctx.clearRect(0, 0, 550, 400);//clears the canvas for redrawing the scene.
ctx.fillStyle = "rgba(0,200,0,1)";//coloring the rectangle
ctx.fillRect = (x, 20, 50, 50);//drawing the rectangle
ctx.restore();//this restores the canvas to it's original state when we saved it on (at the time) line 18
x += 5; //increment the x position by some numeric value
var loopTimer = setTimeout('draw('+x+','+y+')', 2000);// setTimeout is a function that
</script>
</head>
<body>
<button onclick="animate(0,0)">Draw</button>
<canvas id="canvas" width="550" height="400"></canvas>
</body>
Any idea what I'm doing wrong?
I just had a look at the tutorial link. I will give if a major thumbs down as it demonstrates how not to animate and how not to do many other things in Javascript.
First the script tag and what is wrong with it
// type and language default to the correct setting for javascrip
// <script type="application/javascript" language="javascript">
<script>
function anim(x,y) {
// get the canvas once. Getting the canvas for each frame of an
// animation will slow everything down. Same for ctx though will not
// create as much of a slowdown it is not needed for each frame
// var canvas = document.getElementById('canvas');
// var ctx = canvas.getContext('2d');
// Dont use save unless you have to. It is not ok to add it if not needed
// ctx.save();
// dont use literal values, canvas may change size
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(0,200,0,1)";
// this line is wrong should be ctx.fillRect(x, 20, 50, 50). It is correct in the video
ctx.fillRect = (x, 20, 50, 50);//drawing the rectangle
// restore not needed
//ctx.restore();
x += 5; //increment the x position by some numeric value
// creating a string for a timer is bad. It invokes the parser and is slooowwwwww...
// For animations you should avoid setTimeout altogether and use
// requestAnimationFrame
// var loopTimer = setTimeout('draw('+x+','+y+')', 2000);
requestAnimationFrame(draw);
// you were missing the closing curly.
}
</script>
There is lots more wrong with the tut. It can be excused due to it being near 5 years old. You should look for more up todate tutorials as 5 years is forever in computer technology.
Here is how to do it correctly.
// This script should be at the bottom of the page just befor the closing body tag
// If not you need to use the onload event to start the script.
// define a function that starts the animation
function startAnimation() {
animating = true; // flag we are now animating
x = 10;
y = 10;
// animation will start at next frame or restart at next frame if already running
}
// define the animation function
function anim() {
if (animating) { // only draw if animating
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red"; //coloring the rectangle
ctx.fillRect(x, y, 50, 50); //drawing the rectangle
x += xSpeed;
}
// set animation timer for next frame
requestAnimationFrame(anim);
}
// add a click listener to the start button. It calls the supplied function every time you click the button
startAnimButton.addEventListener("click", startAnimation);
const ctx = canvas.getContext('2d'); // get the 2d rendering context
// set up global variables to do the animation
var x, y, animating;
animating = false; // flag we are not animating
const xSpeed = 50 / 60; // Speed is 50 pixels per second at 60fps
// dont slow the animation down via frame rate
// slow it down by reducing speed.
// You only slow frame rate if the machine
// can not handle the load.
// start the animation loop
requestAnimationFrame(anim);
canvas {
border: 1px solid #666;
}
<!-- don't add events inline -->
<button id="startAnimButton">Draw</button>
<canvas id="canvas" width="512" height="128"></canvas>
is it possible to display a html5-video as part of the canvas?
basically the same way as you draw an Image in the canvas.
context.drawVideo(vid, 0, 0);
thanks!
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');
video.addEventListener('play', function () {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
I guess the above code is self Explanatory, If not drop a comment below, I will try to explain the above few lines of code
Edit :
here's an online example, just for you :)
Demo
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');
// set canvas size = video size when known
video.addEventListener('loadedmetadata', function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
});
video.addEventListener('play', function() {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
<div id="theater">
<video id="video" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" controls></video>
<canvas id="canvas"></canvas>
<label>
<br />Try to play me :)</label>
<br />
</div>
Here's a solution that uses more modern syntax and is less verbose than the ones already provided:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener("play", () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
Some useful links:
MDN Documentation for window.requestAnimationFrame()
Can I use requestAnimationFrame?
Using canvas to display Videos
Displaying a video is much the same as displaying an image. The minor differences are to do with onload events and the fact that you need to render the video every frame or you will only see one frame not the animated frames.
The demo below has some minor differences to the example. A mute function (under the video click mute/sound on to toggle sound) and some error checking to catch IE9+ and Edge if they don't have the correct drivers.
Keeping answers current.
The previous answers by user372551 is out of date (December 2010) and has a flaw in the rendering technique used. It uses the setTimeout and a rate of 33.333..ms which setTimeout will round down to 33ms this will cause the frames to be dropped every two seconds and may drop many more if the video frame rate is any higher than 30. Using setTimeout will also introduce video shearing created because setTimeout can not be synced to the display hardware.
There is currently no reliable method that can determine a videos frame rate unless you know the video frame rate in advance you should display it at the maximum display refresh rate possible on browsers. 60fps
The given top answer was for the time (6 years ago) the best solution as requestAnimationFrame was not widely supported (if at all) but requestAnimationFrame is now standard across the Major browsers and should be used instead of setTimeout to reduce or remove dropped frames, and to prevent shearing.
The example demo.
Loads a video and set it to loop. The video will not play until the you click on it. Clicking again will pause. There is a mute/sound on button under the video. The video is muted by default.
Note users of IE9+ and Edge. You may not be able to play the video format WebM as it needs additional drivers to play the videos. They can be found at tools.google.com Download IE9+ WebM support
// This code is from the example document on stackoverflow documentation. See HTML for link to the example.
// This code is almost identical to the example. Mute has been added and a media source. Also added some error handling in case the media load fails and a link to fix IE9+ and Edge support.
// Code by Blindman67.
// Original source has returns 404
// var mediaSource = "http://video.webmfiles.org/big-buck-bunny_trailer.webm";
// New source from wiki commons. Attribution in the leading credits.
var mediaSource = "http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"
var muted = true;
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info
var video = document.createElement("video"); // create a video element
video.src = mediaSource;
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
video.muted = muted;
videoContainer = { // we will add properties as needed
video : video,
ready : false,
};
// To handle errors. This is not part of the example at the moment. Just fixing for Edge that did not like the ogv format video
video.onerror = function(e){
document.body.removeChild(canvas);
document.body.innerHTML += "<h2>There is a problem loading the video</h2><br>";
document.body.innerHTML += "Users of IE9+ , the browser does not support WebM videos used by this demo";
document.body.innerHTML += "<br><a href='https://tools.google.com/dlpage/webmmf/'> Download IE9+ WebM support</a> from tools.google.com<br> this includes Edge and Windows 10";
}
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
function readyToPlayVideo(event){ // this is a referance to the video
// the video may not match the canvas size so find a scale to fit
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
// the video can be played so hand it off to the display function
requestAnimationFrame(updateCanvas);
// add instruction
document.getElementById("playPause").textContent = "Click video to play/pause.";
document.querySelector(".mute").textContent = "Mute";
}
function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height);
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
video.muted = muted;
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}
function drawPayIcon(){
ctx.fillStyle = "black"; // darken display
ctx.globalAlpha = 0.5;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#DDD"; // colour of play icon
ctx.globalAlpha = 0.75; // partly transparent
ctx.beginPath(); // create the path for the icon
var size = (canvas.height / 2) * 0.5; // the size of the icon
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1; // restore alpha
}
function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
function videoMute(){
muted = !muted;
if(muted){
document.querySelector(".mute").textContent = "Mute";
}else{
document.querySelector(".mute").textContent= "Sound on";
}
}
// register the event
canvas.addEventListener("click",playPauseClick);
document.querySelector(".mute").addEventListener("click",videoMute)
body {
font :14px arial;
text-align : center;
background : #36A;
}
h2 {
color : white;
}
canvas {
border : 10px white solid;
cursor : pointer;
}
a {
color : #F93;
}
.mute {
cursor : pointer;
display: initial;
}
<h2>Basic Video & canvas example</h2>
<p>Code example from Stackoverflow Documentation HTML5-Canvas<br>
Basic loading and playing a video on the canvas</p>
<canvas id="myCanvas" width = "532" height ="300" ></canvas><br>
<h3><div id = "playPause">Loading content.</div></h3>
<div class="mute"></div><br>
<div style="font-size:small">Attribution in the leading credits.</div><br>
Canvas extras
Using the canvas to render video gives you additional options in regard to displaying and mixing in fx. The following image shows some of the FX you can get using the canvas. Using the 2D API gives a huge range of creative possibilities.
Image relating to answer Fade canvas video from greyscale to color
See video title in above demo for attribution of content in above inmage.
You need to update currentTime video element and then draw the frame in canvas. Don't init play() event on the video.
You can also use for ex. this plugin https://github.com/tstabla/stVideo
I started with the answer from 2018 about requestAnimationFrame. However, it has three problems.
unnecessarily complex
there is a new way to handle this task (since about 2020)
attaching an event listener to the play event invites performance issues
First, the event listener can simply be connected to the function that does your desired processing and schedules the next call to itself. There's no need to wrap it in an anonymous function with another call to requestAnimationFrame.
Second, don't use requestAnimationFrame. That function schedules the next call to your callback as quickly as the browser can handle (generally 60Hz), which results in a significant processing workload. video.requestVideoFrameCallback only calls your callback when the video proceeds to its next frame. This reduces the workload when a video runs at less than 60 FPS and obviates the need for any processing at all while the video isn't playing, significantly improving performance.
Third, an event listener attached to the play event will fire whenever you tell the video to video.play(), which you'll do every time you load a new video into the video element and tell it to start playing, and also when you resume playback after using video.pause(). Thus, the video is drawn on the canvas (plus whatever other processing you're doing) once for each time the video has been told to play(), which quickly accumulates.
If you're sure you'll only be playing one video which you'd like to pause and resume, you can toggle play/pause by changing the playback rate, e.g. video.playbackRate = !video.playbackRate. If you'll be loading multiple videos into this element, it's better to forego the play event listener entirely and insert a manual call to step() when you load your first video to get that started. Note that this is active on the video element, not on specific loaded videos, so you'll need to either set and check a flag to ensure that you only call step() when loading the first video, or cancel any active video frame request before making a new one (shown below).
let animation_handle;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener('loadeddata', video_load_callback, false);
function video_load_callback() {
video.cancelVideoFrameCallback(animation_handle);
step()
}
function step() { // update the canvas when a video proceeds to next frame
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
animation_handle = video.requestVideoFrameCallback(step);
}
is it possible to display a html5-video as part of the canvas?
basically the same way as you draw an Image in the canvas.
context.drawVideo(vid, 0, 0);
thanks!
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');
video.addEventListener('play', function () {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
I guess the above code is self Explanatory, If not drop a comment below, I will try to explain the above few lines of code
Edit :
here's an online example, just for you :)
Demo
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var video = document.getElementById('video');
// set canvas size = video size when known
video.addEventListener('loadedmetadata', function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
});
video.addEventListener('play', function() {
var $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
<div id="theater">
<video id="video" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" controls></video>
<canvas id="canvas"></canvas>
<label>
<br />Try to play me :)</label>
<br />
</div>
Here's a solution that uses more modern syntax and is less verbose than the ones already provided:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener("play", () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
Some useful links:
MDN Documentation for window.requestAnimationFrame()
Can I use requestAnimationFrame?
Using canvas to display Videos
Displaying a video is much the same as displaying an image. The minor differences are to do with onload events and the fact that you need to render the video every frame or you will only see one frame not the animated frames.
The demo below has some minor differences to the example. A mute function (under the video click mute/sound on to toggle sound) and some error checking to catch IE9+ and Edge if they don't have the correct drivers.
Keeping answers current.
The previous answers by user372551 is out of date (December 2010) and has a flaw in the rendering technique used. It uses the setTimeout and a rate of 33.333..ms which setTimeout will round down to 33ms this will cause the frames to be dropped every two seconds and may drop many more if the video frame rate is any higher than 30. Using setTimeout will also introduce video shearing created because setTimeout can not be synced to the display hardware.
There is currently no reliable method that can determine a videos frame rate unless you know the video frame rate in advance you should display it at the maximum display refresh rate possible on browsers. 60fps
The given top answer was for the time (6 years ago) the best solution as requestAnimationFrame was not widely supported (if at all) but requestAnimationFrame is now standard across the Major browsers and should be used instead of setTimeout to reduce or remove dropped frames, and to prevent shearing.
The example demo.
Loads a video and set it to loop. The video will not play until the you click on it. Clicking again will pause. There is a mute/sound on button under the video. The video is muted by default.
Note users of IE9+ and Edge. You may not be able to play the video format WebM as it needs additional drivers to play the videos. They can be found at tools.google.com Download IE9+ WebM support
// This code is from the example document on stackoverflow documentation. See HTML for link to the example.
// This code is almost identical to the example. Mute has been added and a media source. Also added some error handling in case the media load fails and a link to fix IE9+ and Edge support.
// Code by Blindman67.
// Original source has returns 404
// var mediaSource = "http://video.webmfiles.org/big-buck-bunny_trailer.webm";
// New source from wiki commons. Attribution in the leading credits.
var mediaSource = "http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"
var muted = true;
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info
var video = document.createElement("video"); // create a video element
video.src = mediaSource;
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
video.muted = muted;
videoContainer = { // we will add properties as needed
video : video,
ready : false,
};
// To handle errors. This is not part of the example at the moment. Just fixing for Edge that did not like the ogv format video
video.onerror = function(e){
document.body.removeChild(canvas);
document.body.innerHTML += "<h2>There is a problem loading the video</h2><br>";
document.body.innerHTML += "Users of IE9+ , the browser does not support WebM videos used by this demo";
document.body.innerHTML += "<br><a href='https://tools.google.com/dlpage/webmmf/'> Download IE9+ WebM support</a> from tools.google.com<br> this includes Edge and Windows 10";
}
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
function readyToPlayVideo(event){ // this is a referance to the video
// the video may not match the canvas size so find a scale to fit
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
// the video can be played so hand it off to the display function
requestAnimationFrame(updateCanvas);
// add instruction
document.getElementById("playPause").textContent = "Click video to play/pause.";
document.querySelector(".mute").textContent = "Mute";
}
function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height);
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
video.muted = muted;
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}
function drawPayIcon(){
ctx.fillStyle = "black"; // darken display
ctx.globalAlpha = 0.5;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#DDD"; // colour of play icon
ctx.globalAlpha = 0.75; // partly transparent
ctx.beginPath(); // create the path for the icon
var size = (canvas.height / 2) * 0.5; // the size of the icon
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1; // restore alpha
}
function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
function videoMute(){
muted = !muted;
if(muted){
document.querySelector(".mute").textContent = "Mute";
}else{
document.querySelector(".mute").textContent= "Sound on";
}
}
// register the event
canvas.addEventListener("click",playPauseClick);
document.querySelector(".mute").addEventListener("click",videoMute)
body {
font :14px arial;
text-align : center;
background : #36A;
}
h2 {
color : white;
}
canvas {
border : 10px white solid;
cursor : pointer;
}
a {
color : #F93;
}
.mute {
cursor : pointer;
display: initial;
}
<h2>Basic Video & canvas example</h2>
<p>Code example from Stackoverflow Documentation HTML5-Canvas<br>
Basic loading and playing a video on the canvas</p>
<canvas id="myCanvas" width = "532" height ="300" ></canvas><br>
<h3><div id = "playPause">Loading content.</div></h3>
<div class="mute"></div><br>
<div style="font-size:small">Attribution in the leading credits.</div><br>
Canvas extras
Using the canvas to render video gives you additional options in regard to displaying and mixing in fx. The following image shows some of the FX you can get using the canvas. Using the 2D API gives a huge range of creative possibilities.
Image relating to answer Fade canvas video from greyscale to color
See video title in above demo for attribution of content in above inmage.
You need to update currentTime video element and then draw the frame in canvas. Don't init play() event on the video.
You can also use for ex. this plugin https://github.com/tstabla/stVideo
I started with the answer from 2018 about requestAnimationFrame. However, it has three problems.
unnecessarily complex
there is a new way to handle this task (since about 2020)
attaching an event listener to the play event invites performance issues
First, the event listener can simply be connected to the function that does your desired processing and schedules the next call to itself. There's no need to wrap it in an anonymous function with another call to requestAnimationFrame.
Second, don't use requestAnimationFrame. That function schedules the next call to your callback as quickly as the browser can handle (generally 60Hz), which results in a significant processing workload. video.requestVideoFrameCallback only calls your callback when the video proceeds to its next frame. This reduces the workload when a video runs at less than 60 FPS and obviates the need for any processing at all while the video isn't playing, significantly improving performance.
Third, an event listener attached to the play event will fire whenever you tell the video to video.play(), which you'll do every time you load a new video into the video element and tell it to start playing, and also when you resume playback after using video.pause(). Thus, the video is drawn on the canvas (plus whatever other processing you're doing) once for each time the video has been told to play(), which quickly accumulates.
If you're sure you'll only be playing one video which you'd like to pause and resume, you can toggle play/pause by changing the playback rate, e.g. video.playbackRate = !video.playbackRate. If you'll be loading multiple videos into this element, it's better to forego the play event listener entirely and insert a manual call to step() when you load your first video to get that started. Note that this is active on the video element, not on specific loaded videos, so you'll need to either set and check a flag to ensure that you only call step() when loading the first video, or cancel any active video frame request before making a new one (shown below).
let animation_handle;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.querySelector("video");
video.addEventListener('loadeddata', video_load_callback, false);
function video_load_callback() {
video.cancelVideoFrameCallback(animation_handle);
step()
}
function step() { // update the canvas when a video proceeds to next frame
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
animation_handle = video.requestVideoFrameCallback(step);
}
I'm trying to make a little browser game where you can shoot bullets.
Right now I am able to make a bullet, but I don't know how to get in moving.
I have done this:
var bullet_id = 1;
var timer_id; // reference of the timer, needed to stop it
var speed = 350; // pixels/second
var period = 10; // milliseconds
var sprite; // the element that will move
var sprite_speed = 0; // move per period
var sprite_position = 315; // pixels
function createbullet() {
var img = document.createElement("img");
img.src = "images/bullet.png";
img.id = "bullet";
img.name = "bullet";
var foo = document.getElementById("fooBar");
foo.appendChild(img);
move(1);
bullet_id++;
}
function animate ()
{
document.getElementById("bullet").style.left=340 + "px";
sprite_position += sprite_speed;
sprite.style.left = sprite_position+'px';
}
function move(direction)
{
if (timer_id) stop();
sprite_speed = speed * period/1000 * direction;
timer_id = setInterval (animate, period);
}
function stop()
{
clearInterval (timer_id);
timer_id = null;
}
function init()
{
sprite = document.getElementById ("bullet"); // the HTML element we will move
animate(); // just to initialize sprite position
}
window.onload = init; // start doing things once the page has loaded */
I tried to add a bullet_id system but I couldn't get it working really.
Here is my html
<a onmousedown="document.jack.src=image2.src;" onmouseup="document.jack.src=image1.src;" onclick="createbullet()"><img id="jack" name="jack" src="/images/jack1.png" /></a>
<div id="fooBar"></div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript">
document.getElementById('jack').addEventListener('click',function(){...})
Ok so maybe I didn't think that one through, have just designed one to see if I could and it works, hope it helps:
/********************************************************/
stg=0
bgx=0
spd=70
buls=0
act=false
/********************************************************/
function ani(){
var int
act=true
bgx-=52
stg++
$('#jack').css('background-position','-52px 0px')
int=setInterval(function(){
if(stg<4){bgx-=52; stg++}
else{ bgx=0; stg=0 }
$('#jack').css('background-position',bgx+'px 0px')
if(stg==4) new Bullet();
if(!stg){
act=false
clearInterval(int)
}
},spd)
}
/********************************************************/
function Bullet(){
var x,img,int
x=52
img=document.createElement('img')
img.src='bullet.png'
img.setAttribute('class','mh posAbs')
img.setAttribute('style','top:0px;left:'+x+'px')
img.setAttribute('id','bul'+buls)
scre.appendChild(img)
img=document.getElementById('bul'+buls)
buls++
int=setInterval(function(){
if(x<300){
x+=13
img.setAttribute('style','top:0px;left:'+x+'px')
}
else{
img.src='exp.png'
clearInterval(int)
setTimeout(function(){ scre.removeChild(img) },100)
}
},spd)
}
/********************************************************/
$(document).ready(function(){
$('html').keydown(function(){
if(!act){
if(event.keyCode==13) ani();
}
})
$('html').click(function(){
if(!act) ani();
})
})
/********************************************************/
<div id="scre" class="posRel">
<div id="jack"></div>
</div>
<style>
#jack{
width:52px;
height:37px;
background:url('02.png') no-repeat;
background-position:0px 0px;
background-size:auto 100%
}
</style>
Ok so what's happening above is every time you click or press Enter, the firing animation is called, which is animated in stages and when it gets to a certain stage it calls upon the Bullet() constructor to create a new Object or bullet.
While creating the bullet, the constructor generates an <img> and gives it a unique id based upon the buls variable, which is then incremented to keep the id's unique.
This is the most important part:
img=document.getElementById('bul'+buls)
It will NOT work without it as any references to img in the code after it will refer to the last img created and not say:- 'bullet 5 of 10 that are on screen'.
Once created the Bullet object handles the movement of the image it is referenced to, removing the need to move it with any other code...
P.S. The $('html').keydown(...) acts as an auto-fire!