作者:haha20101030 | 来源:互联网 | 2023-09-15 06:20
这是一个关于h5的video视频播放标签来做视频播放截取视频画面的问题。需求是这样的:要渲染一个视频资源列表,在列表中获取视频的画面来做列表的封面。看到这个需求就想,为什么要在列表
这是一个关于h5的video视频播放标签来做视频播放截取视频画面的问题。
需求是这样的:要渲染一个视频资源列表,在列表中获取视频的画面来做列表的封面。看到这个需求就想,为什么要在列表里截取视频画面做封面,为什么不是后端返回图片url呢?没有那么多为什么,做出来就是了。
首先是找百度,怎么截取h5视频的画面,搜了一遍,大都是通过canvas来画出来的。基本实现过程是:通过video标签把视频加载进来,设置display:none; 把video标签隐藏起来,然后通过canvas的drawImage方法把视频画面画出来,得到一个base6位的图片编码,然后将这个base64位的编码赋给img标签的src即可。
先来一串代码:
html:
"output">
js:
captureImage() {
const output = document.getElementById('output')
const video = document.getElementById('video')
const canvas = document.createElement('canvas')
canvas.width = video.videoWidth * 0.3
canvas.height = video.videoHeight * 0.3
const img = new Image()
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
const dataUrl = canvas.toDataURL('image/png')
img.src = dataUrl
output.appendChild(img)
},
这是截取一个视频画面的实现代码,可以监听视频加载好了之后执行captureImage() 方法(我这里是基于vue的写法,应该比较好理解)。那接下来进入主题,渲染视频列表,如何去画每个视频的画面呢?这里我写了个公共的方法:
html:
for="(item, index) in items" :key="index">
"video-cover">
{
{captureImage(item.id, index,
'items')}}
![]()
"item.cover">
js:
// videoId: 视频标签的id; index: 列表数据的索引;key: this.$data读取列表数据的key
captureImage(videoId, index, key) {
const self = this
setTimeout(function () {
const videoEle = document.getElementById(videoId)
const canvas = document.createElement('canvas')
canvas.width = 265
canvas.height = 180
canvas.getContext('2d').drawImage(videoEle, 0, 0, canvas.width, canvas.height)
const dataUrl = canvas.toDataURL('image/png')
self.$set(self.$data[key][index], 'cover', dataUrl)
}, 100)
}
这是基于vue的写法,首先写一个截取视频画面的公共方法,在进行列表渲染时,每渲染一个item,调用一次该方法(也可以在updated钩子里循环调用),传入对应的视频标签的id以便获取dom节点。这里加了延时器,一开始没加的时候,发现获取到的videoEle是null,可能是该方法执行的时候,video标签还没渲染好,给个小小的延时就可以了(不知有没有更好的解决方法,就先这样做着了,如果路过的大神有更好的解决方案,还望赐教)。
以上的做法可以在列表里渲染出视频画面做封面了,高兴了一小会。后来发现,有bug!!!由于浏览器加载视频速度的问题,导致列表中有些封面画出来是一张透明的图片,甚至有时候全部都是透明的图片,可能是画的时候,视频画面还没加载。后来试了好多方法,最终做了小小的优化:
html:
for="(item, index) in items" :key="index">
class="
video-
cover"> <
video :
id="
item.
id" :
src="
item.
src"
x-
webkit-
airplay="
allow"
autoplay preload="
auto"
style="
display:
none">
video> { {captureImage(item.id, index,
'items')}}
![]()
"item.cover">
js:
// videoId: 视频标签的id; index: 列表数据的索引;key: this.$data读取列表数据的key
captureImage(videoId, index, key) {
const self = this
setTimeout(function () {
const videoEle = document.getElementById(videoId)
const canvas = document.createElement('canvas')
canvas.width = 265
canvas.height = 180
videoEle.addEventListener('timeupdate', function () {
canvas.getContext('2d').drawImage(videoEle, 0, 0, canvas.width, canvas.height)
const dataUrl = canvas.toDataURL('image/png')
self.$set(self.$data[key][index], 'cover', dataUrl)
videoEle.pause()
}, false)
}, 100)
}
这里做了小小的改变,就是在渲染视频列表是让ta自动播放(autoplay),然后在画封面的方法里通过video的timeupdate事件来监听视频播放位置的改变,然后在进行canvas的绘制,绘制完调用video的pause()方法,暂停播放,这样就能保障每个item都能画出不是透明的图片啦。
The end:可算是把视频列表的封面都画出来了,美中不足的是,视频加载的速度还是没法控制,列表中有些视频加载的慢的,会延时好几秒才画出来。
以上就是H5 video标签列表渲染用canvas截取视频画面做封面的一个不完美的方法,如果路过的亲有更好的解决方法,望分享,么么哒。