1

场景

需要用视频的某一帧作为预览图

思路

创建video对象,加载视频元数据,然后用canvas绘制video的画面。

实现细节

1 创建video对象,加载元数据,然后监听必要事件

const getVideoFirstFrame = (videoUrl: string) => {
  const video = document.createElement("video")
  video.preload = "metadata"
  video.src = videoUrl

  video.addEventListener("loadedmetadata", () => {
    // 设置视频时间到第1秒(取决于你想获取第几秒的画面)
    video.currentTime = 1
  })

  // currentTime修改后,会触发seeked事件
  video.addEventListener("seeked", () => {

  }, { once: true }) // 只监听一次
}

2 创建canvas对象,然后绘制video画面

const getVideoFirstFrame = (videoUrl: string) => {
  const video = document.createElement("video")
  video.preload = "metadata"
  video.src = videoUrl

  video.addEventListener("loadedmetadata", () => {
    // 设置视频时间到第1秒(取决于你想获取第几秒的画面)
    video.currentTime = 1
  })

  // currentTime修改后,会触发seeked事件
  video.addEventListener("seeked", () => {
    const canvas = document.createElement("canvas")
    canvas.width = video.videoWidth
    canvas.height = video.videoHeight

    const ctx = canvas.getContext("2d")
    ctx?.drawImage(video, 0, 0)

    // 转换为base64(这里已经获取到视频画面了)
    const dataUrl = canvas.toDataURL("image/jpeg")

  }, { once: true }) // 只监听一次
}

3 绘制操作是异步的,修改函数返回promise

const getVideoFirstFrame = (videoUrl: string) => {
  return new Promise((resolve, reject) => {
    const video = document.createElement("video")
    video.preload = "metadata"
    video.src = videoUrl

    video.addEventListener("loadedmetadata", () => {
      // 设置视频时间到第1秒(取决于你想获取第几秒的画面)
      video.currentTime = 1
    })

    // currentTime修改后,会触发seeked事件
    video.addEventListener("seeked", () => {
      try {
        const canvas = document.createElement("canvas")
        canvas.width = video.videoWidth
        canvas.height = video.videoHeight

        const ctx = canvas.getContext("2d")
        ctx?.drawImage(video, 0, 0)

        // 转换为base64(这里已经获取到视频画面了)
        const dataUrl = canvas.toDataURL("image/jpeg")

        // 清理
        video.remove()
        canvas.remove()

        resolve(dataUrl)
      } catch (err) {
        reject(err)
      }
    }, { once: true }) // 只监听一次

    // 错误处理
    video.addEventListener("error", (err) => {
      reject(new Error(`视频加载失败: ${err}`))
    })
  })
}

4 调用

<script setup lang="ts">
onMounted(async () => {
  const videoUrl = "https://vjs.zencdn.net/v/oceans.mp4"
  const frame = await getVideoFirstFrame(videoUrl)
  img.value = frame
})
</script>

<template>
  <img v-if="img" :src="img" />
</template>

5 验证结果
图片

注意事项

如果你的视频地址是跨域的,canvas将无法绘制视频内容
你需要做两件事:
1 让后台设置允许跨域的响应头
Access-Control-Allow-Origin: * 或指定具体域名

2 设置video.crossOrigin = "anonymous"
否则浏览器会报SecurityError错误:
SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.


热饭班长
3.7k 声望434 粉丝

先去做,做出一坨狗屎,再改进。