uniapp获取视频第一帧做封面图

5,907 阅读5分钟

uniapp获取视频第一帧做封面图

需求:uniapp获取视频第一帧做封面图。
介绍:通过canvas来截取视频的某一帧,然后把这一帧转成base64编码的图片格式即可,canvas需要在document中获取,但是uniapp没有documen元素,所以这时候就要使用renderjs来获取documen元素。
场景:uniapp。

renderjs

先介绍一下renderjs: renderjs是一个运行在视图层的js。
官方介绍renderjs的主要作用有2个:

  1. 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
  2. 在视图层操作dom,运行 for web 的 js库。
  3. 仅支持在APP(vue页面)H5页面使用。nvue无法使用。

解释一下:因为uniapp的逻辑层和视图层是分离的,所以我们无法在vue代码中获取到这个视图层的documen。这种机制就造成了逻辑层和视图层的通信阻塞。renderjs运行在视图层,直接就可以操作dom元素了。

基础的使用方式:

  • moudule这个名称后面是需要拿去使用的。
<script module="test" lang="renderjs">
	export default {
		mounted() {
			// ...
		},
		methods: {
			// ...
		}
	}
</script>

下面来介绍如何通过renderjs和canvas获取视频第一帧

  • 当srcVideo变化时候会触发canvas的getVideoCanvas方法
  • canvas对应renderjs模块module的名称
  • video给宽高都是0,是因为点击封面图我会让他自动全屏播放,这样video元素就不会占住布局位置
<template>
	     <video style="width: 0px; height: 0px;"  class="preview-file" id='video_play' ref="video_play" :src="videoSrc" 
	     @fullscreenchange="onVideoFullScreenChange" autoplay></video>
	
	    <view style="display: flex; flex-wrap: wrap;" :prop="srcVideo" :change:prop="canvas.getVideoCanvas">  
	        <view class="upload-video" v-for="(item1, index1) in srcVideo" :key="index1">  
	            <view class="image-view">  
	                <!-- 获取到的封面图 -->
	                <image style="width: 520rpx; height: 320rpx; border: 1px solid;" :src="item1.imgSrc"></image>  
	            </view>  
	        </view>  
	    </view>
</template>

tip:红色标注的值必须一致 image.png

vue代码

  • onVideoFullScreenChange : 视频全屏状态改变触发的函数
  • getVideoCover : renderjs传来的数据
  • 计时器为了触发srcVideo发生变化,从而触发renderjs。
  • 唯一要思考的点:使用本文代码的时候,只要控制好什么时候改变srcVideo就可以了,比如我选择完视频后,改变srcVideo数据,那么就获取到了视频第一帧。
<script>
    export default {
        data() {
        return {
            srcVideo: [], //视频存放的地址
            videoSrc: null, //视频src
            playVideoing: null, //video本身
        }
    },
    created() {
        //计时器为了触发srcVideo发生变化,从而触发renderjs
        setTimeout(() => {  
            let videoFileObj = {  
                imgSrc: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4',  
            }  
            this.srcVideo.push(videoFileObj)  
        })
    },
    methods: {
            //视频全屏状态改变触发的函数
            onVideoFullScreenChange(e) {  
                if (this.videoSrc && !e.detail.fullScreen) {  
                    this.videoSrc = null;  
                    this.playVideoing.stop();  
                }  
            },
            getVideoCover({index,cover}) {  
                this.srcVideo[index].imgSrc = cover
            },
        }
    }

<script>

renderjs模块

  • module的命名在上面的html标签里面会用到
  • instance拿到整个html标签元素
  • getVideoCanvas是用来接收逻辑层的数据
  • 利用documen创建video元素,进行自动播放,加上静音,来获取帧数
  • currentTime选择第几秒,如果切出来是黑色的话,那么换一下秒数。这里我选择了第40秒,因为很多视频可能第一秒是黑色画面不太友好。
  • toDataURL把数据转成base64编码格式,直接在图片上渲染就行了。

drawImage(img,sx,sy,swidth,sheight,x,y,width,height)

  1. img:image是画布绘制的图像源,绘制到画布上的元素,可以是canvasElement,imageElement,svgImageElement ,videoElement等一系列具有图像的元素。
  2. sx:绘制裁剪的图像源的x 坐标位置;
  3. sy:绘制裁剪的图像源的y坐标位置;
  4. sWidth:绘制裁剪的图像源的宽度;
  5. sHeight:绘制裁剪的图像源的高度;
  6. dx:目标源在canvas画布上绘制的左上角的x坐标;
  7. dy:目标源在canvas画布上绘制的左上角的y坐标;
  8. width:目标源在canvas画布上绘制的宽度,会自动根据图像源截取的宽度对比做缩放;
  9. height:目标源在canvas画布上绘制的高度,会自动根据图像源截取的高度对比做缩放;
<script module="canvas" lang="renderjs">
export default {
  methods: {
    // 接受逻辑层的数据
    getVideoCanvas(newVal, oldValue, instance) {
      //先判断有没有数据
      if (Array.isArray(newVal) && newVal.length > 0) {
        newVal.forEach((item, index) => {
          // 创建video标签
          let video = document.createElement("video")
          // 设置为自动播放和静音
          video.setAttribute('autoplay', 'autoplay')
          video.setAttribute('muted', 'true')
          // 宽高看自己想要获取多大的,随意设置
          video.setAttribute("width", "300px")
          video.setAttribute("height", "200px")
          video.setAttribute('crossOrigin', 'anonymous')
          video.setAttribute("src",
              "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
          )
          // 选择第几秒
          video.currentTime = 40
          // 创建 canvas 元素和 2d 画布
          let canvas = document.createElement('canvas')
          video.addEventListener("loadeddata", function () {
            const canvas = document.createElement("canvas")
            let width = video.width //canvas的尺寸和图片一样
            let height = video.height
            canvas.width = width
            canvas.height = height
            canvas.getContext('2d').drawImage(video, 0, 0, width,
                height) // 绘制canvas
            let imgSrc = canvas.toDataURL("image/jpeg") //获取base46格式的图片文件
            // 下面这个可以转成二进制流
            // 	let imgText = canvas.toBlob(res => {
            // 	console.log('res', res);
            // })
            instance.callMethod('getVideoCover', {
              index,
              cover: imgSrc
            })
          })
        })
      }
    }
  }
}
</script>

image.png 总体代码:

<template>
	<view>
	     <video style="width: 0px; height: 0px;"  class="preview-file" id='video_play' ref="video_play" :src="videoSrc" 
	     @fullscreenchange="onVideoFullScreenChange" autoplay></video>
	
	    <view style="display: flex; flex-wrap: wrap;" :prop="srcVideo" :change:prop="canvas.getVideoCanvas">  
	        <view class="upload-video" v-for="(item1, index1) in srcVideo" :key="index1">  
	            <view class="image-view">  
	                <!-- 获取到的封面图 -->
	                <image style="width: 520rpx; height: 320rpx; border: 1px solid;" :src="item1.imgSrc"></image>  
	            </view>  
	        </view>  
	    </view>
	</view>
</template>

<script>
    export default {
        data() {
        return {
            srcVideo: [], //视频存放的地址
            videoSrc: null, //视频src
            playVideoing: null, //video本身
        }
    },
    created() {
        //计时器为了触发srcVideo发生变化,从而触发renderjs
        setTimeout(() => {  
            let videoFileObj = {  
                imgSrc: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4',  
            }  
            this.srcVideo.push(videoFileObj)  
        })
    },
    methods: {
            //视频全屏状态改变触发的函数
            onVideoFullScreenChange(e) {  
                if (this.videoSrc && !e.detail.fullScreen) {  
                    this.videoSrc = null;  
                    this.playVideoing.stop();  
                }  
            },
            getVideoCover({index,cover}) {  
                this.srcVideo[index].imgSrc = cover
            },
        }
    }
</script>	

<script module="canvas" lang="renderjs">
export default {
  methods: {
    // 接受逻辑层的数据
    getVideoCanvas(newVal, oldValue, instance) {
      //先判断有没有数据
      if (Array.isArray(newVal) && newVal.length > 0) {
        newVal.forEach((item, index) => {
          // 创建video标签
          let video = document.createElement("video")
          // 设置为自动播放和静音
          video.setAttribute('autoplay', 'autoplay')
          video.setAttribute('muted', 'true')
          // 宽高看自己想要获取多大的,随意设置
          video.setAttribute("width", "300px")
          video.setAttribute("height", "200px")
          video.setAttribute('crossOrigin', 'anonymous')
          video.setAttribute("src",
              "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
          )
          // 选择第几秒
          video.currentTime = 40
          // 创建 canvas 元素和 2d 画布
          let canvas = document.createElement('canvas')
          video.addEventListener("loadeddata", function () {
            const canvas = document.createElement("canvas")
            let width = video.width //canvas的尺寸和图片一样
            let height = video.height
            canvas.width = width
            canvas.height = height
            canvas.getContext('2d').drawImage(video, 0, 0, width,
                height) // 绘制canvas
            let imgSrc = canvas.toDataURL("image/jpeg") //获取base46格式的图片文件
            // 下面这个可以转成二进制流
            // 	let imgText = canvas.toBlob(res => {
            // 	console.log('res', res);
            // })
            instance.callMethod('getVideoCover', {
              index,
              cover: imgSrc
            })
          })
        })
      }
    }
  }
}
</script>

以上介绍就是uniapp如何获取视频第一帧做封面图。