从视频复制到 javascript 中的 canvas
Copying from video to canvas in javascript
我正在尝试加载视频并将其中的缩略图绘制到 canvas 中。我遇到的问题是我找不到在视频图像准备好复制时触发的事件。当 loadeddata 事件触发时,大小设置正确但 canvas 图像为空白。如果我取消注释定时器延迟一秒钟然后 canvas 被填充,但没有延迟它不是。是否有我忽略的事件,或者其他一些告诉视频元素何时能够复制的方式?我试过 load、canplay、canplaythrough、loadeddata、progress,但 none 似乎来得正是时候。
<html>
<head>
<script>
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('loadeddata', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-]/g, '');
height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-]/g, '');
image = document.getElementById('image');
image.width = width;
image.height = height;
//setTimeout(draw, 1000);
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
</script>
</head>
<body >
<video id="video" autostart="false" ></video>
<canvas id="image"></canvas>
</body>
</html>
如果有人有更好的解决方案,我会欢迎它,但我确实找到了一种方法来做我想做的事,通过加载元数据,进行搜索,然后在“搜索”事件监听器中复制图像.我正在使用 Chrome,从我读到的内容来看,这可能不是一个通用的解决方案,但由于目前我只支持 Chrome,这似乎可以满足我的需求。
<html>
<head>
<script>
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('loadedmetadata', x => video.currentTime = 0);
video.addEventListener('seeked', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-9]/g, '');
height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-9]/g, '');
image = document.getElementById('image');
image.width = width;
image.height = height;
//setTimeout(draw, 1000);
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
</script>
</head>
<body >
<button onclick="copy('asd.mp4')">Copy asd.mp4</button>
<br />
<video id="video" preload="metadata" autostart="false" ></video>
<canvas id="image"></canvas>
</body>
</html>
虽然 loadeddata
事件会在当前帧的数据加载后立即触发,但它并没有给出任何实际含义的提示。
但在实践中,这意味着在某些情况下,或在某些浏览器中,它可能能够呈现第一帧 - 尽管您不能依赖它。
解决方案是寻找一个事件,该事件稍后会在 loading/processing 视频播放过程中触发。
这些事件发生的顺序如下:
- 加载开始
- 持续时间变化
- 加载元数据
- 加载数据
- 进度
- 可以玩
因此,如果我们简单地采用 canplay
事件而不是 loadeddata
,事情应该会按预期进行。
这是一个例子:
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('canplay', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = parseInt(document.defaultView.getComputedStyle(video).width);
height = parseInt(document.defaultView.getComputedStyle(video).height);
image = document.getElementById('image');
image.width = width;
image.height = height;
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
copy("https://www.w3schools.com/html/mov_bbb.mp4")
<video id="video" autostart="false"></video>
<canvas id="image"></canvas>
编辑
在使用不同的浏览器进行一些测试后,我的解决方案似乎在 Chrome 中失败了。尽管如此,我还是找到了一种解决方法,它也适用于 Chrome.
有一个相当未记录的参数,您可以将其附加到视频的 URL,让您指定视频的开始位置甚至范围 (#t=
)。
现在,如果您将开始位置设置为较低的值(例如 0.000001),您仍然会看到视频的第一帧并且您可以绘制到 canvas。
但是我的测试还表明,如果您使用 canplaythrough
事件而不是 canplay
或 loadeddata
,这仅适用于 FireFox 和 Chrome。
修改后的示例如下:
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('canplaythrough', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = parseInt(document.defaultView.getComputedStyle(video).width);
height = parseInt(document.defaultView.getComputedStyle(video).height);
image = document.getElementById('image');
image.width = width;
image.height = height;
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
copy("https://www.w3schools.com/html/mov_bbb.mp4#t=0.000001")
<video id="video" autostart="false"></video>
<canvas id="image"></canvas>
我正在尝试加载视频并将其中的缩略图绘制到 canvas 中。我遇到的问题是我找不到在视频图像准备好复制时触发的事件。当 loadeddata 事件触发时,大小设置正确但 canvas 图像为空白。如果我取消注释定时器延迟一秒钟然后 canvas 被填充,但没有延迟它不是。是否有我忽略的事件,或者其他一些告诉视频元素何时能够复制的方式?我试过 load、canplay、canplaythrough、loadeddata、progress,但 none 似乎来得正是时候。
<html>
<head>
<script>
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('loadeddata', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-]/g, '');
height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-]/g, '');
image = document.getElementById('image');
image.width = width;
image.height = height;
//setTimeout(draw, 1000);
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
</script>
</head>
<body >
<video id="video" autostart="false" ></video>
<canvas id="image"></canvas>
</body>
</html>
如果有人有更好的解决方案,我会欢迎它,但我确实找到了一种方法来做我想做的事,通过加载元数据,进行搜索,然后在“搜索”事件监听器中复制图像.我正在使用 Chrome,从我读到的内容来看,这可能不是一个通用的解决方案,但由于目前我只支持 Chrome,这似乎可以满足我的需求。
<html>
<head>
<script>
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('loadedmetadata', x => video.currentTime = 0);
video.addEventListener('seeked', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-9]/g, '');
height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-9]/g, '');
image = document.getElementById('image');
image.width = width;
image.height = height;
//setTimeout(draw, 1000);
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
</script>
</head>
<body >
<button onclick="copy('asd.mp4')">Copy asd.mp4</button>
<br />
<video id="video" preload="metadata" autostart="false" ></video>
<canvas id="image"></canvas>
</body>
</html>
虽然 loadeddata
事件会在当前帧的数据加载后立即触发,但它并没有给出任何实际含义的提示。
但在实践中,这意味着在某些情况下,或在某些浏览器中,它可能能够呈现第一帧 - 尽管您不能依赖它。
解决方案是寻找一个事件,该事件稍后会在 loading/processing 视频播放过程中触发。
这些事件发生的顺序如下:
- 加载开始
- 持续时间变化
- 加载元数据
- 加载数据
- 进度
- 可以玩
因此,如果我们简单地采用 canplay
事件而不是 loadeddata
,事情应该会按预期进行。
这是一个例子:
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('canplay', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = parseInt(document.defaultView.getComputedStyle(video).width);
height = parseInt(document.defaultView.getComputedStyle(video).height);
image = document.getElementById('image');
image.width = width;
image.height = height;
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
copy("https://www.w3schools.com/html/mov_bbb.mp4")
<video id="video" autostart="false"></video>
<canvas id="image"></canvas>
编辑
在使用不同的浏览器进行一些测试后,我的解决方案似乎在 Chrome 中失败了。尽管如此,我还是找到了一种解决方法,它也适用于 Chrome.
有一个相当未记录的参数,您可以将其附加到视频的 URL,让您指定视频的开始位置甚至范围 (#t=
)。
现在,如果您将开始位置设置为较低的值(例如 0.000001),您仍然会看到视频的第一帧并且您可以绘制到 canvas。
但是我的测试还表明,如果您使用 canplaythrough
事件而不是 canplay
或 loadeddata
,这仅适用于 FireFox 和 Chrome。
修改后的示例如下:
var video, image, width, height;
function copy(src) {
video = document.getElementById('video');
video.addEventListener('canplaythrough', x => finishCopy());
video.src = src;
}
function finishCopy() {
width = parseInt(document.defaultView.getComputedStyle(video).width);
height = parseInt(document.defaultView.getComputedStyle(video).height);
image = document.getElementById('image');
image.width = width;
image.height = height;
draw();
}
function draw() {
let ctxt = image.getContext('2d');
ctxt.drawImage(video, 0, 0, width, height);
}
copy("https://www.w3schools.com/html/mov_bbb.mp4#t=0.000001")
<video id="video" autostart="false"></video>
<canvas id="image"></canvas>