每日分享:使用Canvas从视频文件提取关键帧作为封面

365 阅读2分钟

每天学习一个技巧,今天是截取视频的关键帧

主要用到了canvas技术

1.首先准备好html

<body>
    <input type="file" name="" id="" /> //上传视频文件
    <div class="img"></div> // 存放视频帧图片
    <script src="./index.js"></script>
</body>

2.主要函数captureFrame(获取视频关键帧的函数)

参数是视频地址,第几秒
返回一个对象 { blob,url }

function captureFrame(vdoFile, time = 0) {
  return new Promise((res) => {
    const vdo = document.createElement("video"); // 创建一个video元素
    vdo.currentTime = time; // 设置视频的起始时间
    vdo.muted = true; // 先静音才能播放,某些浏览器有限制
    vdo.autoplay = true; // 因为视频并没有放在页面上,所以并不会一直播放下去
    // 在视频加载好预备播放的事件中去画canvas获取图片
    vdo.oncanplay = async () => {
      const frame = await drawVideo(vdo);
      duration = vdo.duration; // 获取视频的总时长
      res(frame); // 将获取到的图片信息对象返回
    };
    // 获取当前文件的一个内存URL
    vdo.src = URL.createObjectURL(vdoFile);
  });
}

3.主要函数drawVideo(canvas画关键帧(异步))

function drawVideo(vdo) {
  return new Promise((res) => {
    const cvs = document.createElement("canvas"); // 创建canvas元素
    const ctx = cvs.getContext("2d"); // 返回一个canvas渲染的上下文
    cvs.width = vdo.videoWidth; // 获取视频的宽度
    cvs.height = vdo.videoHeight; // 获取视频的高度
    ctx.drawImage(vdo, 0, 0, cvs.width, cvs.height); // 从视频的左上角画满视频的宽高
    cvs.toBlob((blob) => {
      res({
        blob,
        url: URL.createObjectURL(blob),
      });
    });
  });
}

4.开始操作

创建全局参数

// 获取页面的input
const ipt = document.querySelector("input[type=file]");
// 获取视频的总时长
let duration = 0;

监听input的change事件

创建图片预览图的函数并转为base64格式

// 创建预览图
function createPreview(frame) {
  const img = document.createElement("img");
  img.src = frame.url;
  document.querySelector(".img").appendChild(img);
  img.onclick = () => {
    let base64 = imageTobase64(img); // 将图片转为base64格式,方便与后端对接
    console.log(base64);
  };
}

将图片转为base64格式的函数

// 将图片转为base64
function imageTobase64(img) {
  const cvs = document.createElement("canvas");
  cvs.width = img.width;
  cvs.height = img.height;
  const ctx = cvs.getContext("2d");
  ctx.drawImage(img, 0, 0, cvs.width, cvs.height);
  const ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
  let dataUrl = cvs.toDataURL("image/jpeg" + ext);
  return dataUrl;
}

input的change变化触发

ipt.onchange = async (e) => {
  const file = e.target.files[0];
  const frame = await captureFrame(file); // 这一步主要是先获取视频的总时长
  for (let i = 1; i < duration; i++) {    // 然后在从下一秒开始
    const frame = await captureFrame(file, i);
    createPreview(frame);
  }
};