前言
使用vue3开发项目时,碰上需要做一个视频流列表的页面,最开始是想获取所有列表数据后创建对应的video标签,这样默认获取第一帧作为封面,同时暂停视频减轻不断请求的压力。但开发后发现视频就算暂停后也会继续请求视频流,多个视频反而会导致页面卡顿。
方案
手动获取视频的第一帧,通过canvas将其绘画出来后作为封面,隐藏销毁video标签。
部分代码块
<div ref="videoContainerRef" absolute top-5px bottom-5px left-5px right-5px>
<vue3VideoPlay v-if="!video.isSingleShowPoster" :id="video.id"
v-bind="createVideoParams(video.name, video.url, false, 'auto')"
@loadeddata="getVideoPoster(video)" />
<img v-else w-full h-full :src="video.poster" alt="preview">
</div>
<script setup lang="ts">
const video = reactive({
name: "video",
id: "id", // 用作video标签的id
isSingleShowPoster: false, // 是否显示poster
poster: '', // 自动获取poster第一帧
url:"",
}) // 由于修改数据后dom树能够及时变化,涉及到双向绑定,所以数据需要是响应式
// preload要选择 auto 预加载,否则可能会获取不到第一帧
function createVideoParams(title: string, url: string, autoPlay: boolean = false, preload: 'auto' | 'metadata' | 'none' = 'none') {
return {
width: "100%", // 播放器高度
height: "100%", // 播放器高度
color: "#5B8BB3", // 主题色
title: title, // 视频名称
type: "m3u8",
src: url, // 视频源
muted: true, // 静音
webFullScreen: false,
autoPlay: autoPlay, // 自动播放
volume: 0, // 默认音量大小
control: false, // 是否显示控制
preload: preload,
controlBtns: ["fullScreen"], // 显示所有按钮,
};
}
const videoDialogContainerRef = ref()
async function getVideoPoster(info: any) {
const video = document.getElementById(info.id)! as HTMLVideoElement;
video.currentTime = 0
const canvas = document.createElement('canvas');
// 绘制时画布大小最好和video标签大小一样(大小不一样可能绘制出来会糊)(代码关系可能会导致获取不到video大小导致canvas宽高为0,
// 如果画布的高度或宽度是0,那么绘制出来的图片字符为"data:,")
// 如果获取不到video标签的 绘制出来的图片会黑屏
// 如果能直接获取获取video大小最好,这里通过video外部容器的高度近似获取video大小
const { clientWidth, clientHeight } = videoDialogContainerRef.value;
canvas.width = clientWidth;
canvas.height = clientHeight;
const ctx = canvas.getContext('2d', { alpha: false })!
ctx?.drawImage(video, 0, 0, clientWidth, clientHeight);
const img = canvas.toDataURL('image/jpeg', 1);
canvas.remove();
video.src = ""; // 绘制完成后停止video流请求,通过v-if移除dom树
info.isSingleShowPoster = true;
info.poster = img;
}
</script>
注意
监听函数一定得是"onloadeddata",如果preload=none: 页面加载时视频不加载可能绘制出来的图片就会使纯黑。
虽然设置了onloadeddata事件才去画它的第一帧,按理说应该视频已经加载出了第一帧了,但是由于没有设置preload=auto并没有截图到第一帧。(好像不管有没有preload都会执行onloadeddata)
所以要想截图第一帧就必须让它自动加载。因为各浏览器对preload不设置时的默认值可能不同还是手动设置一下比较好。