借助 gif.js 实现前端视频转GIF

3,300 阅读2分钟

基础原理

  • gif库:用于生成gif动图
  • video转gif原理:
    • 设置时间函数 setInterval() 、通过canvas截取video元素固定时间间隔的帧画面
    • 调用 gif.addFrame(),将截取画面作为 gif 中的一帧追加到gif图中
    • 监听视频播放完毕的 onended 事件,调用 gif.render() 完成对gif添加帧的渲染
    • 监听 gif.on('finished', function(blob){}) 将blob转为可下载链接并下载到本地

划重点:由于这里是通过固定时间间隔,截图视频帧画面来生成GIF图,所以需要让视频自然播放直到结束。(当然你也可以设置video倍速播放)

gif.js 基础api

var gif = new GIF({
  quality: 10, // gif 清晰度,越低越清晰
  workerScript: './gif.worker.js', // 借助worker运行video解决视频跨域问题
  debug: true // 开启调试模式
});

// 将canvas节点追加到gif帧中,delay 是每一帧的时间间隔
gif.addFrame(canvasElement, {delay: 200});

// 监听渲染完成,返回 blob 文件流
gif.on('finished', function(blob) {
  const url = URL.createObjectURL(blob);
});

// 将每一帧渲染成一张完成的gif
gif.render();

逻辑代码

// 本地上传视频文件,生成视频url
input.addEventListener('change', () => {
    const videoFile = e.target.files[0];
    const videoUrl = URL.createObjectURL(videoFile)
    video.setAttribute('src', videoUrl);  
})
// 创建GIF
const videoGif = new GIF({
    workers: 2,
    quality: 1,
    workerScript: './gif.worker.js',
    debug: true
});

// 设定时间间隔
const delay = 100; 
// video 事件监听与播放
video.addEventListener('loadedmetadata', () => {
    // 部分视频尺寸可能会很大,可以再加载完成时设置样式缩小其尺寸
    const width = this.video.videoWidth / 5;
    const height = this.video.videoHeight / 5;
    video.setAttribute('style', `width:${width}px;height:${height}px`);
})

video.addEventListener('ended' = () => {
    // 视频播放完毕,清除时间函数
    clearInterval(timer);
    
    // 将追加的gif帧,渲染成完整图
    videoGif.render()
    
    // 监听渲染完毕,并输出 gif 地址
    videoGif.on('finished', blob => {
        const gifImg = URL.createObjectURL(blob);
    })
})

video.play(); // 播放视频
// 创建canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');

// 设置时间函数
const timer = setInterval(() => {
    // 画布的宽高与视频宽高保持一致,延迟取上面定义的延迟时间
    canvas.width = width;
    canvas.height = height;
    canvas.delay = delay
    canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); 
    
    // 将当前画面帧追加到 gif中
    const imgImage = new Image();
    imgImage.src = canvas.toDataURL("image/png");
    
    imgImage.onload = (e) => {
      // 绘制底色  
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      
      // 绘制当前帧
      ctx.drawImage(imgImage, 0, 0, canvas.width, canvas.height);
      
      // 当前帧追加到gif中
      gif.addFrame(canvas, {copy: true, delay: canvas.delay});
    }
}, delay)
// 下载转换后的gif图
download.addEventListener('click', () => {
    const aLink = document.createElement('a');
    aLink.setAttribute('download', 'img')
    aLink.setAttribute('href', gifImg)
    aLink.click()
})

最终效果

DEMO 在线体验

testcase.gif