crop picture in the middle with javascript - javascript

my image is 800 x 600px, I need to crop this pic in the midlle (600 x 600px)
I create this code but is not working
function takeSnapshot(){
// Here we're using a trick that involves a hidden canvas element.
var hidden_canvas = document.querySelector('canvas'),
context = hidden_canvas.getContext('2d');
// Make a copy of the current frame in the video on the canvas.
context.drawImage(video, 100, 0, 600, 600);
// Turn the canvas image into a dataURL that can be used as a src for our photo.
return hidden_canvas.toDataURL('image/png');
}
this is my full code
document.addEventListener('DOMContentLoaded', function () {
// References to all the element we will need.
var video = document.querySelector('#camera-stream'),
image = document.querySelector('#snap'),
start_camera = document.querySelector('#start-camera'),
controls = document.querySelector('.controls'),
take_photo_btn = document.querySelector('#take-photo'),
delete_photo_btn = document.querySelector('#delete-photo'),
download_photo_btn = document.querySelector('#download-photo'),
error_message = document.querySelector('#error-message');
// The getUserMedia interface is used for handling camera input.
// Some browsers need a prefix so here we're covering all the options
navigator.getMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
if(!navigator.getMedia){
displayErrorMessage("Your browser doesn't have support for the navigator.getUserMedia interface.");
}
else{
// Request the camera.
navigator.getMedia(
{
video: true
},
// Success Callback
function(stream){
// Create an object URL for the video stream and
// set it as src of our HTLM video element.
video.srcObject=stream;
// Play the video element to start the stream.
video.play();
video.onplay = function() {
showVideo();
};
},
// Error Callback
function(err){
displayErrorMessage("There was an error with accessing the camera stream: " + err.name, err);
}
);
}
// Mobile browsers cannot play video without user input,
// so here we're using a button to start it manually.
start_camera.addEventListener("click", function(e){
e.preventDefault();
// Start video playback manually.
video.play();
showVideo();
});
take_photo_btn.addEventListener("click", function(e){
e.preventDefault();
var snap = takeSnapshot();
// Show image.
image.setAttribute('src', snap);
image.classList.add("visible");
// Enable delete and save buttons
delete_photo_btn.classList.remove("disabled");
download_photo_btn.classList.remove("disabled");
// Set the href attribute of the download button to the snap url.
download_photo_btn.href = snap;
// Pause video playback of stream.
video.pause();
});
delete_photo_btn.addEventListener("click", function(e){
e.preventDefault();
// Hide image.
image.setAttribute('src', "");
image.classList.remove("visible");
// Disable delete and save buttons
delete_photo_btn.classList.add("disabled");
download_photo_btn.classList.add("disabled");
// Resume playback of stream.
video.play();
});
function showVideo(){
// Display the video stream and the controls.
hideUI();
video.classList.add("visible");
controls.classList.add("visible");
}
function takeSnapshot(){
// Here we're using a trick that involves a hidden canvas element.
var hidden_canvas = document.querySelector('canvas'),
context = hidden_canvas.getContext('2d');
// Make a copy of the current frame in the video on the canvas.
context.drawImage(video, 100, 0, 600, 600);
// Turn the canvas image into a dataURL that can be used as a src for our photo.
return hidden_canvas.toDataURL('image/png');
}
function displayErrorMessage(error_msg, error){
error = error || "";
if(error){
console.error(error);
}
error_message.innerText = error_msg;
hideUI();
error_message.classList.add("visible");
}
function hideUI(){
// Helper function for clearing the app UI.
controls.classList.remove("visible");
start_camera.classList.remove("visible");
video.classList.remove("visible");
snap.classList.remove("visible");
error_message.classList.remove("visible");
}
});

To crop try...
var imgData = context.getImageData(100, 0, 600, 600); // start from 100px and a width of 600px
context.putImageData(imgData, 0, 0); // or place it back at X=100px, its up to you.
Btw, your URL returned should be...
For PNG:
URL=hidden_canvas.toDataURL('image/png').replace('image/png','image/octet-stream');
For JPG:
URL=hidden_canvas.toDataURL("image/jpeg",1.0);

It doesn't change anything manu, cropping is simple...
var Data=context.getImageData(100, 0, canvas.width-100,canvas.height); // grab the portion you want
context.clearRect(0,0,canvas.width,canvas.height); // clear the old image so the crop shows better
context.putImageData(Data,0,0); // paste it in

I think this should work for you:
let data = context.getImageData(100, 0, canvas.width / 2, canvas.height);
context.clearRect(0, 0, canvas.width, canvas.height);
context.putImageData(data, 0, 0);

Related

HTML5 video element request stay pending forever (on chrome in mobile) when toggle over front-rare camera

I'm developing an app where users can capture photo using a front/rare camera. it working perfectly but when toggle over camera front/rare var playPromise = videoStream.play() is gone in pending state. some times promise get resolve, the camera is working sometimes not.
this issue occurs only in chrome browser not in mozila and firefox
try {
stopWebCam(); // stop media stream when toggle over camera
stream = await navigator.mediaDevices.getUserMedia({video: true});
/* use the stream */
let videoStream = document.getElementById('captureCandidateId');
videoStream.srcObject = stream;
// videoStream.play();
var playPromise = videoStream.play();
if (playPromise !== undefined) {
playPromise.then(_ => {
// Automatic playback started!
// Show playing UI.
})
.catch(error => {
// Auto-play was prevented
// Show paused UI.
});
}
};
} catch(err) {
/* handle the error */
console.log(err.name + ": " + err.message);
}
let stopWebCam = function (pictureType) {
setTimeout(()=>{
let videoStream = document.getElementById('captureCandidateId');
const stream = videoStream.srcObject;
if (stream && stream.getTracks) {
const tracks = stream.getTracks();
tracks.forEach(function(track) {
track.stop();
});
}
videoStream.srcObject = null;
}, 0)
}
Here, I drafted a piece of code for you, this is much more simple and smaller approach than what you are trying to do. I am just taking the stream from the video element and drawing it to canvas. Image can be downloaded by right clicking.
NOTE: Example does not work in StackOverflow
<video id="player" controls autoplay></video>
<button id="capture">Capture</button>
<canvas id="canvas" width=320 height=240></canvas>
<script>
const player = document.getElementById('player');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const captureButton = document.getElementById('capture');
const constraints = {
video: true,
};
captureButton.addEventListener('click', () => {
// Draw the video frame to the canvas.
context.drawImage(player, 0, 0, canvas.width, canvas.height);
});
// Attach the video stream to the video element and autoplay.
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
player.srcObject = stream;
});
</script>
If you want, you can also make some edits according to your needs, like:
Choose which camera to use
Hide the video stream
Add a easier method to download the photo on your device
You can also add a functionality to upload the photo straight to the server if you have one

How to access document.body from a script inside of document.write

Help! I am working on an application which downloads a report from an API which gives me HTML as part of the JSON return. Because it returns JSON I cannot simply open this in a new window.
In this script I am injecting this code:
let tmp = result.data;
let spot = tmp.indexOf('</body>');
let insert = `<div style="position:fixed; top:200px;left:20px; width:200px;font-size:15pt">
<div class="camera">
<video id="video" style='display:hidden'>Video stream not available.</video>
<button onClick='takepicture()'>Take photo</button>
</div>
<canvas id="canvas" style='display:none;'></canvas>
<!--<button onClick='genPDF()'>PDF</button>-->
Press CTRL+S to save page as HTML
<img id="testDownloadImage" src='' width=600 height=400 />
</div>
<script type='text/javascript'>
alert(document.body.innerHtml)
// function clearphoto() {
// var context = canvas.getContext('2d');
// context.fillStyle = "#AAA";
// context.fillRect(0, 0, canvas.width, canvas.height);
// var data = canvas.toDataURL('image/png');
// // photo.setAttribute('src', data);
// }
// Capture a photo by fetching the current contents of the video
// and drawing it into a canvas, then converting that to a PNG
// format data URL. By drawing it on an offscreen canvas and then
// drawing that to the screen, we can change its size and/or apply
// other changes before drawing it.
async function takepicture() {
let video = document.getElementById('video');
let canvas = document.getElementById('canvas');
let displayMediaOptions={video: true,displaySurface:'browser', logicalSurface:true, cursor:'never', audio: false};
try{
video.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
// video.play();
var context = canvas.getContext('2d');
width=window.innerWidth;
height=window.scrollHeight;
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
var data = canvas.toDataURL('image/jpg'); //.replace(/^data:image\/(png|jpg);base64,/, "");
// photo.setAttribute('src', data);
document.getElementById('testDownloadImage').src=data;
// location.href=data;
} else {
// clearphoto();
}
}
catch(err) {
console.log("An error occurred: " + err);
}
finally{
let tracks = video.srcObject.getTracks();
tracks.forEach(track => track.stop());
video.srcObject = null;
}
}
</script>
`
let newStr = [tmp.slice(0, spot), insert, tmp.slice(spot)].join('');
document.write(newStr);
to allow me to add a print screen option so that the report can be imaged. I have tried other utilities to save the HTML as a PDF or even saving the page but because the report uses a lot of XSLT and scripting to display the results these methods fail, leaving me with the print screen as the only option.
WebRTC handles this aptly, with one minor difficult: Because I am using document.write I am unable to find the document.body even though the code I am passed is a properly formed page ( etc).
How can I use the inserted script to display the page and capture it as an image?
Thanks
This is not a genuine answer sadly but I resolved the problem by moving the entire write to an iFrame with the script on the outside. I then got the scrollHeight of the iFrame document and resized the frame to match that, effectively making it appear as if the document occupied the entire page. This way I can place my script outside the iFrame and still use it to capture the entire page, which is what I was shooting for.
Thank you for any comments. As of now this is no longer needed.

HTML5 getUserMedia is not working with scientific microscopy camera Lumenera

I am using microscopy camera Lumenera INFINITY1-2 (https://www.lumenera.com/infinity1-2.html) and from the camera description it tells that it can be integrated into 3rd party software packages using TWAIN support. I try to capture image from web page using HTML5 but it gives me error.
I also tried to check video streaming with Adobe Flash but camera stream is not displayed properly and captured images is quite broken (in fact it is empty)
I checked camera's drivers and they all installed and works. Native camera application also captures images well.
Question, is it possible to capture images in the web browser from the microscopy camera like that? It is being recognized by browser as a device but video stream is not working.
Please see below script (code is getting from here https://github.com/jhuckaby/webcamjs/blob/master/DOCS.md)
Thank you in advance
window.addEventListener("load", function () {
// [1] GET ALL THE HTML ELEMENTS
var video = document.getElementById("vid-show"),
canvas = document.getElementById("vid-canvas"),
take = document.getElementById("vid-take");
navigator.mediaDevices.getUserMedia({ video: true })
.then(function (stream) {
// [3] SHOW VIDEO STREAM ON VIDEO TAG
video.srcObject = stream;
video.play();
take.addEventListener("click", function () {
// Create snapshot from video
var draw = document.createElement("canvas");
draw.width = video.videoWidth;
draw.height = video.videoHeight;
var context2D = draw.getContext("2d");
context2D.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
// Output as file
var anchor = document.createElement("a");
anchor.href = draw.toDataURL("image/png");
anchor.download = "webcam.png";
anchor.innerHTML = "Click to download";
canvas.innerHTML = "";
canvas.appendChild(anchor);
var imageBase64 = draw.toDataURL("image/png").replace("data:image/png;base64,", "");
document.getElementById("hdn_ImageByte").setAttribute("value", imageBase64);
});
})
.catch(function (err) {
debugger;
console.log(err)
document.getElementById("vid-controls").innerHTML = "Please enable access and attach a camera";
});
});
</script>```

How to play a cutscene in HTML5 canvas [duplicate]

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);
}

How to display video tag in a canvas element [duplicate]

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);
}

Categories

Resources