前言
在很多视频网站,上传视频后,会让用户选取视频的某一帧的画面作为视频的封面。
那么这个功能是怎么实现的,本文就介绍下如何通过选择的视频生成某一帧的画面。
1. 前置知识
实践之前先了解下需要用到的方法及事件。
1.1 createObjectURL
URL.createObjectURL()
静态方法会创建一个 DOMString
,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document
绑定。这个新的 URL 对象表示指定的 File
对象或 Blob
对象。
例如:通过URL.createObjectURL 生成临时链接地址
将地址在浏览器中打开,即可访问本地资源,页面关闭就失效
当我们使用input
标签进行选择视频文件时,如何传递给video标签使用,就需要createObjectURL
方法创建一个临时的链接,提供给video
使用.
1.2 HTMLMediaElement: canplay事件
canplay
事件在终端可以播放媒体文件时(但估计还没有加载足够的数据来播放媒体直到其结束,即后续可能需要停止以进一步缓冲内容)被触发。
当video标签有地址视频,就可以指定视频的时间及播放,此时就需要对应的事件进行监听。canplay
事件保证video正常触发,这样才能给下一个阶段使用。
1.2 HTMLCanvasElement.toBlob方法
HTMLCanvasElement.toBlob()
方法创造 Blob
对象,用以展示 canvas 上的图片;这个图片文件可以被缓存或保存到本地(由用户代理自行决定)。
使用canvas从video元素中获取画面帧,使用toBlob方法得到blob对象,用以保存上传。
2. 具体实现
2.1 实现思路
具体实现过程:
- 文件选择: 用户通过
<input type="file">
标签选择了一个视频文件。 - 事件监听: 为这个
input
元素添加了onchange
事件监听器,这样当用户选择文件后,就能触发对应的处理函数。 - 创建临时链接: 在处理函数中,通过
URL.createObjectURL(file)
为选中的视频文件创建一个临时的URL,这个URL可以直接用于<video>
元素的src
属性,以便在页面上播放视频。 - 视频准备: 设置
<video>
元素的currentTime
属性来指定要捕获的帧的时间点,并监听canplay
事件以确保视频已准备好可以播放。 - 捕获帧: 当视频准备好后,使用
<canvas>
元素和绘图API来绘制视频帧。这通常涉及到将<video>
元素绘制到<canvas>
上,然后利用<canvas>
的toBlob
方法将当前帧转换为一个Blob
对象。 - 处理结果:
toBlob
方法返回的是一个Promise
,它解析为一个包含捕获帧数据的Blob
对象。这个Blob
对象可以用于多种用途,比如上传到服务器、预览或保存到本地。 - 预览和上传: 一旦得到了
Blob
对象,就可以使用它创建一个新的临时URL来预览捕获的帧,或者上传到服务器。
2.1 代码实现
定义页面结构:
<input type="file" id="ipt" />
具体逻辑实现:
const inputNode = document.getElementById('ipt')
inputNode.onchange = async (e) => {
const file = e.target.files[0]
const res = await captureFrame(file)
const img = document.createElement('img')
img.src = res.url
document.body.appendChild(img)
}
// 使用canvas绘制
function drawVideo(videoNode) {
return new Promise((resolve, reject) => {
const cvs = document.createElement('canvas');
const ctx = cvs.getContext('2d')
cvs.width = videoNode.videoWidth
cvs.height = videoNode.videoHeight
ctx.drawImage(videoNode, 0, 0, cvs.width, cvs.height)
cvs.toBlob(blob => {
resolve({
blob,
url: URL.createObjectURL(blob)
})
})
})
}
// 视频 使用video -> 画入canvas -> 提取图片
function captureFrame(videoFile, time = 0) {
return new Promise(resolve => {
const videoNode = document.createElement('video')
videoNode.currentTime = time
videoNode.muted = true // 静音 可以播放
videoNode.autoplay = true
// 是什么,能够读取本地的数据的url,开头是blob
videoNode.src = URL.createObjectURL(videoFile)
// 需要时间准备好时间进行播放即可
videoNode.oncanplay = async () => {
const res = await drawVideo(videoNode)
resolve(res)
}
})
}
效果如下:
3. 总结
最后总结一下,就是通过file
对象生成临时链接,提供给视频的video
,进行播放设置,canvas直接进行绘制转换,就可以得到每一帧的画面。
如有错误,请指正O^O!
原文链接:https://juejin.cn/post/7352079398072746047 作者:一诺滚雪球