使用ts实现一个截取视频截图的函数,按需自取!

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);
        });
    });
}