Reputation: 29
So I am trying to overlay a HTML canvas element over a 500x500 video. I always want the video to be 500x500. In my code, I've set the canvas height and width to be the same as the video height and width so they overlay right on top of one another. Unfortunately, whenever I run this, the canvas element always outputs a width of 800 and a height of 600. I believe this may have something to do with the size of the original video. However, I'm really not sure why my canvas elements won't pick up on those values. I also tried directly setting my canvas height and width to 500 but when I do that it won't overlay right on top of the video. I was just wondering if anyone knew why this might be occurring and if there is anything striking out to you in my code where I am setting these elements that is causing this to occur. I have also looked at other stack overflow posts before making this one and tried to get some context from those solutions but nothing has worked for me yet.
This is my .php file:
document.getElementById("bounding-tool").addEventListener("click", function() {
const canvas = document.getElementById("canvas");
canvas.style.visibility = "visible";
canvas.style.opacity = 0;
//canvas.style.position = relative;
})
function create_canvas() {
const video_frame = document.getElementById("video_frame");
//const video = document.getElementById("video");
const video = document.querySelector("video#video, #video video");
//let rect = video.getBoundingClientRect();
//console.log(rect.bottom, rect.left, rect.top, rect.right)
video.addEventListener('loadedmetadata', (event) => {
const canvas = document.getElementById("canvas");
canvas.height = 438;
canvas.width = 584;
const { videoWidth, videoHeight } = video;
// We're assuming that the width is the larger than the height of video.
const shrinkFactor = canvas.height / videoHeight * 100;
const dimensions = {
width: Math.floor(videoWidth / 100 * shrinkFactor),
height: Math.floor(videoHeight / 100 * shrinkFactor)
};
const offset = (dimensions.width - canvas.width) / 2;
var context = canvas.getContext('2d');
const loop = () => {
context.save();
context.fillRect(0,0,video.VideoHeight,video.videoWidth);
context.drawImage(video,0,0,video.VideoHeight,video.videoWidth);
create_bounding_tool(canvas);
video_frame.append(canvas);
canvas.style.visibility = "hidden";
//canvas.style.opacity = "0";
context.restore();
context.fillRect(0,0,canvas.height,canvas.width);
// bind event handler to clear button
//context.clearRect(0, 0, canvas.width, canvas.height);*/
}
loop();
});
}
#video_frame{
display: inline-flex;
position: relative;
margin-left: 7em;
width: 584px;
height: 438px;
flex: 0 0 584px;
margin-right: 0.5rem;
margin-top: 7em;
}
#playback_frame{
display:flex;
}
#video{
width: 100%;
height: 100%;
object-fit: cover;
/*opacity: 0.5;*/
border: 4px solid #0033A0;
/*box-shadow: 0px 0px 7px grey;*/
}
#canvas{
position: absolute;
/* margin-top: 4.4em;*/
top: 0;
left: 0;
width: 100%;
height: 100%;
/*pointer-events: none;*/
opacity: 0.5;
border: 5px solid red;
}
<head>
<title> </title>
<meta charset="UTF-8">
<meta name="description" content="Video Determiner">
<meta name="keywords" content="Video Determiner" />
<meta name="author" content="Video Determiner" />
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link href="../css/style.css" rel="stylesheet">
<link href="https://vjs.zencdn.net/7.10.2/video-js.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/videojs-seek-buttons/dist/videojs-seek-buttons.css"/>
</head>
<div id="video_frame">
<video class = "video-js vjs-big-play-centered" controls id="video" width = "584" height = "438" data-setup="{}">
<source id ="source" src="http://grochtdreis.de/fuer-jsfiddle/video/sintel_trailer-480.mp4" type="video/mp4"">
<p class="vjs-no-js"> To view this video please enable JavaScript, and consider upgrading to a web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
<canvas class = "video-wrapper_canvas" id = "canvas"></canvas>
</div>
Upvotes: 0
Views: 1590
Reputation: 20944
I've made a demo below which shows the video element with a canvas overlay. The canvas renders the video with the same position and size as the video itself. If you Run code snippet and then Full page you'll get the best result.
To make sure that both the video and canvas are the same size, set the container to 500x500 with the video and canvas to 100% width and height.
I've use object-fit: cover;
to make the video fully expand the width and height of the container.
Now, the video might be a lot larger than 500x500, so we'll need to convert a (for example) 1920x1080 video to a 500x500 viewport. To do this we'll need to make some calculations.
We start off by calculating the shrink factor. The height of the video will be the same height of the canvas, but the width of the video must shrink the same amount of pixels to make sure that the aspect ratio of the video stays the same.
Therefor we calculate height of canvas / height of video x 100
. This gives us percentage that the video must shrink to fit the height of the canvas while keeping the right aspect ratio.
Now because the width of the video is still larger than the width of the canvas, we'll need to offset the video a bit to the left to center our video. We do this by doing (new video width - canvas width) / 2
. This is the number in pixels the video needs to move left.
Use these values to draw the video in canvas element with the right dimensions.
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
canvas.height = 500;
canvas.width = 500;
const context = canvas.getContext('2d');
video.addEventListener('loadedmetadata', () => {
const { videoWidth, videoHeight } = video;
// We're assuming that the width is the larger than the height of video.
const shrinkFactor = canvas.height / videoHeight * 100;
const dimensions = {
width: Math.floor(videoWidth / 100 * shrinkFactor),
height: Math.floor(videoHeight / 100 * shrinkFactor)
};
const offset = (dimensions.width - canvas.width) / 2;
const loop = () => {
context.clearRect(
0,
0,
canvas.width,
canvas.height
);
context.drawImage(
video,
0 - offset, // Move it to the left to compensate for the width.
0,
dimensions.width,
dimensions.height
);
requestAnimationFrame(loop);
};
loop();
});
const compare = document.getElementById('compare');
compare.addEventListener('input', event => {
const value = event.target.value;
video.style.opacity = 1 - Number(value);
canvas.style.opacity = value;
});
body {
display: flex;
align-items: flex-start;
}
.video-wrapper {
position: relative;
width: 500px;
height: 500px;
flex: 0 0 500px;
margin-right: 0.5rem;
}
.video-wrapper__video {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.5;
border: 5px solid blue;
}
.video-wrapper__canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
opacity: 0.5;
border: 5px solid red;
}
.slider {
padding: 1rem;
border: 1px solid #d0d0d0;
border-radius: 3px;
background-color: #f0f0f0;
margin-left: 0.5rem;
}
.slider label {
display: flex;
font-family: sans-serif;
}
.label-video {
color: blue;
}
.label-canvas {
color: red;
}
<div class="video-wrapper">
<video class="video-js vjs-big-play-centered video-wrapper__video" controls id="video" width="500" height="500" data-setup="{}">
<source src="http://grochtdreis.de/fuer-jsfiddle/video/sintel_trailer-480.mp4"/>
</video>
<canvas class="video-wrapper__canvas" id="canvas"></canvas>
</div>
<div class="slider">
<label>
<span class="label-video">Video</span>
<input id="compare" type="range" min="0.1" max="0.9" step="0.1" value="0.5">
<span class="label-canvas">Canvas</span>
</label>
</div>
Upvotes: 2