本地视频预览组件
<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截取视频第一帧作为预览图片