我们可能经常看到这样的一个功能,在视频播放的过程中,动态的将视频中的某一帧截下来。
或者一些更常见的比如视频首帧截图。那这样的一个功能,具体是怎样实现的?一起来做做吧。
整体的方案大致是这样:首先需要准备几个子功能模块。首先,得有视频,能播视频,并且可以做到实时的监听视频的进度,其次就是截图了。核心功能有了,除此之外呢?既然是动态的,所以最好还要有一个动态的dom结构插入方便展示结果,还得能方便的更换视频,也就是视频上传,当然我们为了简单化,这里不涉及后台,但以上的功能同样都能实现。
首先,可以播放视频,通过HTML5的video来实现,具体如何呢?
下面是一个video标签,这样一个标签,就可以播放视频。
<video src="../assets/123.mp4" controls class='video'></video>
video如何监听进度的呢?
video的事件实际上非常多,这里就不一一列举了,我们看MDN,如下图:
可以看到这里面有一个事件是timeupdate,这就是我们想要的进度监听。
_self.video.addEventListener('timeupdate', function (e) {
console.log('当下进度为: ' + this.currentTime);
console.log('视频总时长为: ' + this.duration);
});
其次,处理截图,处理图片比如对图片做压缩做剪切做各种各样的处理,日常的软件其实就是类似photoshop这样的图片软件,或者更加原始的就是通过画图软件,但程序怎么做呢?画图画图,可能这时候你也意识到了,没有错,基本都可以通过HTML5的画布(canvas)来实现。看一段下面的代码。
// 创建画布
createCanvas () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
this.canvas = canvas;
this.ctx = ctx;
}
这时候再配合一下刚刚的timeupdate进度监听,就可以基本实现我们的核心功能了。
var nowTime = 0;
_self.video.addEventListener('timeupdate', function (e) {
if(this.currentTime > nowTime){
_self.ctx.drawImage(this, 0, 0, this.videoWidth, this.videoHeight);
let src = _self.canvas.toDataURL('image/jpeg');
_self.appendImg(src);
nowTime += _self.speed;
}
});
但这时候你会发现,截出来的图不对,好像只截了一部分,这时候就需要设置一下画布的大小了。但因为传入不同的视频,呈现的画面大小其实都是不一致的,所以需要根据视频上传后或者加载后的第一帧,取这时候的video大小。
这时候video也同样提供一个当video加载完视频文件的第一帧时的事件。即loadeddata。看看:
_self.video.addEventListener('loadeddata', function () {
_self.canvas.width = this.videoWidth;
_self.canvas.height = this.videoHeight;
});
这样我们基本上就完成了核心的功能。
接下来,再附上一个视频上传的功能,这时候dom结构稍微做一下变动,这其实就是一个典型的隐式表单提交。
<div class='container'>
<div class='video-container'>
<input type="file" id="input" hidden />
<video src="../assets/123.mp4" controls class='video'></video>
<button>上传视频</button>
</div>
<div class='screenshot-list screenshotList'></div>
</div>
// video文件上传
upLoadFile () {
let _self = this;
_self.input.addEventListener('change', function (e) {
_self.file = this.files[0];
_self.src = window.URL.createObjectURL(_self.file);
_self.video.src = _self.src;
});
_self.btn.addEventListener('click', function () {
_self.input.click();
});
}
当然,动态截取出来的图片需要让我们的感官有直观的体验,当然是将之动态的展现出来。当然加点Css3的动画效果就更好了。
appendImg (src) {
var screenshotList = document.querySelector('.screenshotList');
var oDiv = document.createElement('div'),
img = document.createElement('img');
img.src = src;
oDiv.className = 'pd10 animated bounceInRight';
oDiv.appendChild(img);
screenshotList.insertBefore(oDiv, screenshotList.querySelector('div'));
// screenshotList.appendChild(oDiv);
// screenshotList.scrollTop = screenshotList.scrollHeight;
}
这样基本就完成了我们的功能了。
看一看总体的代码:
class videoCapture {
constructor (options) {
this.Dom = options.Dom;
this.speed = options.speed || 2;
this.input = this.Dom.querySelector('input');
this.btn = this.Dom.querySelector('button');
this.video = this.Dom.querySelector('video');
this.image = this.Dom.querySelector('img');
this.file = '';
this.src = '';
this.initEvent();
}
// video文件上传
upLoadFile () {
let _self = this;
_self.input.addEventListener('change', function (e) {
_self.file = this.files[0];
_self.src = window.URL.createObjectURL(_self.file);
_self.video.src = _self.src;
});
_self.btn.addEventListener('click', function () {
_self.input.click();
});
}
// 创建画布
createCanvas () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
this.canvas = canvas;
this.ctx = ctx;
}
initEvent () {
let _self = this;
_self.upLoadFile();
_self.createCanvas();
_self.video.addEventListener('loadeddata', function () {
_self.canvas.width = this.videoWidth;
_self.canvas.height = this.videoHeight;
});
var nowTime = 0;
_self.video.addEventListener('timeupdate', function (e) {
if(this.currentTime > nowTime){
_self.ctx.drawImage(this, 0, 0, this.videoWidth, this.videoHeight);
let src = _self.canvas.toDataURL('image/jpeg');
_self.appendImg(src);
nowTime += _self.speed;
}
});
}
appendImg (src) {
var screenshotList = document.querySelector('.screenshotList');
var oDiv = document.createElement('div'),
img = document.createElement('img');
img.src = src;
oDiv.className = 'pd10 animated bounceInRight';
oDiv.appendChild(img);
screenshotList.insertBefore(oDiv, screenshotList.querySelector('div'));
// screenshotList.appendChild(oDiv);
// screenshotList.scrollTop = screenshotList.scrollHeight;
}
}
export default videoCapture;
再看一看调用:
layout: false
title: "视频动态截图"
date: 2019-09-19 20:03:48
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="../css/animate.css">
<link rel="stylesheet" href="../css/dynamicScreenshots.css">
<title>视频动态截图</title>
</head>
<body>
<div class='container'>
<div class='video-container'>
<input type="file" id="input" hidden />
<video src="../assets/123.mp4" controls class='video'></video>
<button>上传视频</button>
</div>
<div class='screenshot-list screenshotList'></div>
</div>
<script type='module'>
import videoCapture from '../js/dynamicScreenshots/dynamicScreenshots.js';
let Dom = document.querySelector('.container'),
speed = 2;
new videoCapture({ Dom, speed });
</script>
</body>
</html>
最后呢,当然是上效果了。