获取视频的首帧图片、本地预览视频(首帧图片有时候为透明)

本地视频预览组件

<template>
  <div class="upload-area">
    <div class="upload-card">
      <div class="image" @click="previewVideo">
        <input @change="upload" @click.stop type="file" accept="video/*" class="upload-file" ref="uploadFile">
      </div>
      <div class="title-and-pregress">
        <p class="file-name">
          文件名
          <i class="el-icon-success upload-ok"></i>
        </p>
        <div class="upload-progress">
          <p>上传完成 <span @click="deleteVedio" class="delete">删除</span> <span @click="reUpload" class="re-upload">重新上传</span> </p>
            <div class="progress-bar" :style="progressLength"></div>
          <p>已经上传: 11MB/123MB 当前速度: 1MB/S 剩余时间: 12.3</p>
        </div>
      </div>
    </div>
    <div v-show="showPreview" class="preview-video">
      <i class="el-icon-circle-close" @click="showPreview = false"></i>
      <video v-show="video_url" id="video" poster="封面图路径" :src="video_url" controls="controls" x5-playsinline="" playsinline="" webkit-playsinline preload="auto"></video>
    </div>
    <div id="output"></div>
  </div>
</template>

<script>
function dataURLtoBlob(dataurl) {
      var arr = dataurl.split(','),
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
      while(n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr],{type:mime});
};

export default {
  data() {
    return {
      uploadStatus: 0,  // 0 => 失败, 1 => 上传中, 2 => 上传成功
      uploadPercent: 10,
      showPreview: false,
      uploadFile: null,
      video_url: '',
    }
  },
  directives: {
   select:{//默认选中
      inserted:function (el,binding) {
        console.log('el', el);
        el.click();
      }
   },
   focus:{//默认聚焦
      inserted:function (el,binding) {
              el.focus();
      }
   }
  },
  created() {
    // setInterval(() => {
    //   this.uploadPercent += 5;
    // }, 1000);
  },
  computed: {
    progressLength() {
      return {
        background: `linear-gradient(90deg,#3FB370 ${this.uploadPercent}%,#DDE2EB ${this.uploadPercent}%)`
      }
    },
  },
  methods: {
    deleteVedio() {
      this.video_url = ''
    },
    previewVideo() {
      this.showPreview = true;
    },
    reUpload() {
      console.log('reupload');
      this.$refs.uploadFile.click();
    },
    upload() {
      let files = this.$refs.uploadFile.files[0];
      console.log('file', files);
      var url = URL.createObjectURL(files);
      this.video_url = url;
      setTimeout(() => {
        
        this.getCapture()
      }, 1000);
    },
    getCapture() {
      let scale = 0.3;
      let output = document.getElementById("output");
      let video = document.getElementById("video");
      var canvas = document.createElement("canvas");
      canvas.width = video.videoWidth * scale;
      canvas.height = video.videoHeight * scale;
      canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
      var img = document.createElement("img");
      img.src = canvas.toDataURL("image/png");
      var poster = dataURLtoBlob(img.src);
      console.log('poster', poster);
      output.appendChild(img);
    }
  }
}
</script>

<style lang="less" scoped>
.upload-card{
  height: 140px;
  margin: 0 32px;
  padding: 38px 34px;
  border-bottom: 1px solid rgba(242,245,250,1);
  display: flex;
  .image{
    width:119px;
    height:68px;
    background: #333;
    .upload-file{
      opacity: 0;
      width: 0;
    }
  }
  .title-and-pregress{
    text-align: left;
    margin-left: 26px;
    height:68px;
    width:688px;
    .file-name{
      font-size:14px;
      // font-family:PingFang SC;
      font-weight:bold;
      color:rgba(77,77,77,1);
      line-height:26px;
      position: relative;
      .upload-ok{
        color: #3FB370;
        position: absolute;
        right: 0;
        top: 6px;
      }
    }
    .upload-progress{
      font-size:12px;
      color:rgba(153,162,170,1);
      line-height:22px;
      .delete{
        color: #FD3E3E;
        margin-left: 14px;
        cursor: pointer;
      }
      .re-upload{
        color: #3FB370;
        margin-left: 14px;
        cursor: pointer;
      }
      .progress-bar{
        width:689px;
        height:2px;
      }
    }
  }
}
.preview-video{
  position: absolute;
  top: 250px;
  left: 50%;
  margin-left: -290px;
  width:585px;
  height:328px;
  background:rgba(0,0,0,.3);
  .el-icon-circle-close{
    position: absolute;
    top: -32px;
    right: -32px;
    font-size: 30px;
    color: rgba(0,0,0,.2);
    // opacity:0.2;
  }
  #video{
    max-height: 100%;
    max-height: 100%;
  }
}
</style>
bug

有时候视频第一针截图为透明图片, 应该不是网络原因, 在视频load后截图也一样,好像和视频文件大小有关,目前不知道怎么处理,假如我只截取视频的第一个chunk再来截取会不会更好呢?有兴趣的可以自行研究,也可以持续关注这篇文章,我有时间会继续探索更新它。

 getFirstChunkCapture(file){
 		// 答案是可以用第一个chunk来获取第一帧图片的, 但是chunk的值不能太小,4m是可以的
    	// const firstChunk = this.file.slice( 0, 1024 * 4000)
      // const url = URL.createObjectURL(firstChunk)
      const url = URL.createObjectURL(file)
      let video = document.createElement("video");
      video.src = url
      setTimeout(() => {
        let scale = 0.3;
        var canvas = document.createElement("canvas");
        canvas.width = video.videoWidth * scale;
        canvas.height = video.videoHeight * scale;
        canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
        var img = document.createElement("img");
        var posterUrl = canvas.toDataURL("image/png");
        console.log('posterUrl', posterUrl);
        var poster = dataURLtoFile(posterUrl, 'poster.png')
        this.uploadImage(poster)
      }, 1000);
    },

代码解析: 你会发现, 用create出来的video来截取第一帧图片就一直会是有问题的, 原因是, 这个video没有在dom中出现, 所以截取出来的图片是透明的, 本文中的截取图片的问题也是因为video标签的v-show属性, 因为video_url赋值后有一段时间,video是没有展示出来的,所以截图为透明, 所以只要给一个很小的video用于陈放videourl就可以了, 上述代码中也有截取视频第一帧来放入video中的, 是我再探索过程中想到的一个小点, 也许有用吧。

然后还有一个优化点就是等到视频加载好了再截图

const video = document.getElementById('video')
      video.onloadedmetadata = function () {
      that.duration = video.duration;
      that.vedioLoaded = true;
}
clearTimeout(this.capTimer)
      if (!this.vedioLoaded) {
        this.capTimer = setTimeout(() => {
          this.getCapture()
        }, 1000);
        return;
      }

文章参考:

1、JS里DataURL、File、Blob及canvas对象间互相转换的方法函数
2、input选择视频或图片本地预览问题
3、H5截取视频第一帧作为预览图片

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值