interface ThumbnailOptions {
source: File; // 视频源文件对象
width?: number; // 缩略图宽度
time?: number; // 裁剪时间点,单位为秒,默认为视频中间位置
format?: string; // 输入的格式,默认为 jpg
}
/**
* 从视频中获取封面图
* @param options 配置项
* @returns 封面图的 Data URL
*/
export async function getVideoThumbnail(options: ThumbnailOptions): Promise<Blob | null> {
const {source, width, time, format = 'jpg'} = options;
return new Promise((resolve, reject) => {
// 创建 video 元素
const video = document.createElement('video');
video.src = URL.createObjectURL(source);
video.crossOrigin = 'anonymous';
video.muted = true;
video.autoplay = false;
// 监听 video 元素的 loadedmetadata 事件
video.addEventListener('loadedmetadata', () => {
// 计算视频的宽度和高度
const videoWidth = video.videoWidth;
const videoHeight = video.videoHeight;
const aspectRatio = videoWidth / videoHeight;
// 计算缩略图的宽度和高度
const thumbnailWidth = width || videoWidth;
const thumbnailHeight = Math.round(thumbnailWidth / aspectRatio);
// 创建 canvas 元素,并设置其宽度和高度
const canvas = document.createElement('canvas');
canvas.width = thumbnailWidth;
canvas.height = thumbnailHeight;
// 获取 canvas 上下文,并绘制视频帧
const ctx = canvas.getContext('2d');
video.currentTime = time != null ? time : video.duration / 2;
video.play();
// 监听 video 元素的 timeupdate 事件
video.addEventListener('timeupdate', () => {
// 暂定
video.pause();
// 在 canvas 上绘制视频帧
ctx?.drawImage(video, 0, 0, thumbnailWidth, thumbnailHeight);
// 将 canvas 转换为 Data URL,并解析为封面图的文件路径
canvas.toBlob((blob) => {
resolve(blob);
}, `image/${format}`);
});
});
// 监听 video 元素的 error 事件
video.addEventListener('error', (err) => {
reject(err);
});
});
}