• 原生JS实现视频截图


    视频截图效果预览

    视频截图效果

    利用Canvas进行截图

    要用原生js实现视频截图,可以利用canvas的绘图功能 ctx.drawImage,只需要获取到视频标签,就可以通过drawImage把视频当前帧图像绘制在canvas画布上。

    const video = document.querySelector('video')
    const canvas = document.createElement('canvas')
    const w = video.videoWidth
    const h = video.videoHeight
    canvas.width = w
    canvas.height = h
    const ctx = canvas.getContext('2d')
    ctx.drawImage(video, 0, 0, w, h)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    接下来,需要把画布转化为图片,canvas提供了两个2D转换为图片的方法:canvas.toDataURL()canvas.toBlob()

    canvas.toDataURL(mimeType, qualityArgument)方法

    toDataURL可以把图片转换成base64格式的图片,是一个同步方法,使用很简单,在上面已经绘制好画布的基础上,只需要下面一行代码就可以获取到当前视频帧的截图了

    const imageUrl = canvas.toDataURL("image/png")
    console.log(imageUrl)
    
    • 1
    • 2

    toDataURL生成的base64图片
    可以看到,它最终生成了一个很长字符串的base64图片地址。

    canvas.toBlob(callback, mimeType, qualityArgument)方法

    这个方法相比上一个方法的优点是它是异步的,所以有一个callback回调,这个callback回调方法默认的第一个参数就是转换好的blob文件信息,本文也想重点介绍这种方法的使用

    先说明一下这个方法的三个参数:

    参数类型是否必传说明
    callbackFunctiontoBlob()方法执行成功后的回调方法,支持一个参数,表示当前转换的Blob对象
    mimeTypeString表示需要转换的图像的mimeType类型。默认值是image/png,还可以是image/jpeg,甚至image/webp(前提浏览器支持)等
    qualityArgumentNumber表示转换的图片质量。范围是0到1。由于Canvas的toBlob()方法转PNG是无损的,因此,此参数默认是没有效的,除非,指定图片mimeType是image/jpeg或者image/webp,此时默认压缩值是0.92

    使用写法如下:

    canvas.toBlob((blob) => {
    	console.log(blob)
    }, 'image/png', 0.92)
    
    • 1
    • 2
    • 3

    可以看到方法执行得到的是当前转换的Blob对象
    Blob对象
    那么剩下的就是要将此Blob对象进一步转化为可供img显示的图片地址。

    将Blob对象转化为图片地址

    下面介绍三种方法进行转化:

    方式一: 通过URL.createObjectURL()方法将Blob转化为URL
    canvas.toBlob((blob) => {
    	const imageUrl = URL.createObjectURL(blob)
    	console.log(1, imageUrl)
    }, 'image/jpeg', 1)
    
    • 1
    • 2
    • 3
    • 4

    如下图所示,转化得到的是一个bold流的图片地址。

    blob流图片地址

    方式二: 通过FileReader将Blob转化为DataURL
    canvas.toBlob((blob) => {
    	const reader = new FileReader()
    	reader.readAsDataURL(blob)
    	reader.onload = () => {
    	  const imageUrl = reader.result
    	  console.log(2, imageUrl)
    	}
    }, 'image/webp', 1)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如下图所示,转化得到的是一个base64的图片地址。

    base64图片地址

    方式三: 通过ajax将Blob上传到服务器
    canvas.toBlob((blob) => {
    	const formData = new FormData()
        formData.append('file', blob) // 这里的'file'是接口接收参数的字段名,需要根据实际情况改变
        const xhr = new XMLHttpRequest()
        xhr.onload = () => {
          const imageUrl = JSON.parse(xhr.responseText).data // 接口回调参数,需要根据实际情况处理
          console.log(3, imageUrl)
        }
        xhr.open('POST', '/api/upload', true) // '/api/upload'是上传接口,需要根据实际情况改变
        xhr.send(formData)
    }, 'image/webp', 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    由此就会将图片上传到你的文件服务器里,最终可以得到一个你自己文件服务器下对应的图片地址。

    toBlob()方法的兼容

    首先,toBlob()方法IE9浏览器不支持,因为Blob数据格式IE10+才支持。

    然后,对于IE浏览器,toBlob()的兼容性有些奇怪,IE10浏览器支持ms私有前缀的toBlob()方法,完整方法名称是msToBlob()。而IE11+,toBlob()方法却不支持。

    但是,我们可以基于toDataURL()方法进行polyfill,性能相对会差一些,JavaScript代码如下:

    if (!HTMLCanvasElement.prototype.toBlob) {
      Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
        value: function (callback, type, quality) {
          var canvas = this
          setTimeout(function() {
            var binStr = atob( canvas.toDataURL(type, quality).split(',')[1] )
            var len = binStr.length
            var arr = new Uint8Array(len)
    
            for (var i = 0; i < len; i++) {
              arr[i] = binStr.charCodeAt(i)
            }
    
            callback(new Blob([arr], { type: type || 'image/png' }))
          })
        }
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    注意事项

    使用外部链接播放视频的话需要在视频标签上设置允许跨域的处理,添加属性crossOrigin='anonymous'即可,

    <video className="videoTag" crossOrigin='anonymous' controls>
         <source src="https://www.w3school.com.cn/example/html5/mov_bbb.mp4" type='video/mp4' />
    video>
    
    • 1
    • 2
    • 3

    或者,在js里处理

    const video = document.querySelector(".videoTag")
    video.setAttribute('crossOrigin', 'anonymous')
    video.load()
    
    • 1
    • 2
    • 3

    否则会报以下错误:
    跨域报错

    完整封装示例

    最后,给出一个利用toBlob进行视频截图,最终获取base64图片地址的封装方法,代码示例如下:

    function getBase64ByVideo(video) {
    	const canvas = document.createElement("canvas")
        const w = video.videoWidth
        const h = video.videoHeight
        canvas.width = w
        canvas.height = h
        return new Promise((resolve, reject) => { // 由于toBlob方法是异步的,所以这里用Promise
          const ctx = canvas.getContext('2d')
          ctx.drawImage(video, 0, 0, w, h)
          canvas.toBlob((blob) => {
            // 通过FileReader将Blob转化为DataURL
            const reader = new FileReader()
            reader.readAsDataURL(blob)
            reader.onload = () => {
              const imageUrl = reader.result
              resolve(imageUrl)
            }
          }, 'image/webp', 1) // 根据需要可以自行配置这里的两个参数
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    调用方法:

    const videoTag = document.querySelector(".videoTag")
    const dataUrl = await getBase64ByVideo(videoTag)
    
    • 1
    • 2
  • 相关阅读:
    公众号开发教程:微信视频预览一半就要收费是怎么做的
    前端之JS篇(十)——BOM概述&定时器&JS执行机制&location&navigator&history
    青岛大学数据结构与算法——第6章
    【SQL性能优化】从磁盘I/O的角度理解SQL查询的成本(优)
    【日积月累】SpringBoot 通过注解@CacheConfig @Cacheable @CacheEvict @CachePut @Caching使用缓存
    Leetcode刷题350. 两个数组的交集 II
    一次Django SSO简单实现
    Internet Explorer 浏览器
    去面试了几家BATJ等N家互联网大厂
    哎,被这个叫做at least once的玩意坑麻了。
  • 原文地址:https://blog.csdn.net/Marco_hui/article/details/134380834