vue项目获取网络视频/图片九宫格列表展示预览组件
今天有点时间,总结下前段时间在vue项目中做的移动端视频/图片上传查看组件遇到的一些问(大)题(坑),组件全部实现过程就不讲了,主要说其中遇到的问题以及解决方法,有不足之处请大神们拍砖指教
问题1:将获取尺寸不一的图片/视频在方格内展示
由于无法确定图片的尺寸大小,就决定使用js来控制样式(不拉伸图片,截取方格展示)
解决思路:将图片相对于父盒子上下左右居中显示,方法很多,选一个自己中意的就好了。js获取图片视频尺寸,若宽比高长,就以设置高为100%,反之就设置宽为100%,父盒子设置溢出隐藏。简单粗暴,就这样。
css实现 : https://vip.kingdee.com/article/7600
问题2:在mounted生命周期内获取图片/视频尺寸问题
理想没毛病,现实总是会挖很多坑等你。美美的想着,dom在mounted生命周期内已经挂在完了,这时候我去获取图片尺寸应该没毛病可以吧,得,是获取到,宽高全都是0!原因就是Dom已经加载了没错,可因为网络请求原因,图片/视频资源还没加载回来,以致数据都是0。图样图森破,尝试了很多方法,$nextTick也不管用。setTimeout设置一段时间后请求可以解决这个问题,但是对于请求时间的不确定性,以及强迫症晚期不允许我这么粗暴处理,继续想想方法,那就等图片/视频资源加载后再去获取宽高啊,聪明如我,哈哈哈
calcImgStyle(){
//设置图片在方格内展示
let imgArr = document.querySelectorAll(".img")
for(let i=0;i<imgArr.length;i++){
imgArr[i].onload = ()=>{
if(imgArr[i].width>imgArr[i].height){
imgArr[i].style.height = '100%'
}else {
imgArr[i].style.width = '100%'
}
}
}
}
问题3:如何获取视频图片作为预览封面
拿到的数据就是视频的url地址,并没有缩略图,怎么破,自己创造呗,网上找了很多资源,大家都差不多,将video隐藏,使用canvas来绘制一张video一帧,使用toDataURL将地址给创建的img图片的src就好。
几个注意点:
- 获取视频尺寸问题,我是选择在 loadeddata 事件后获取,目前没啥毛病
- 虽然你设置了autoplay在Safari视频也不会自动播放(我的是有音频的文件,不能静音),但是一定要设置autoplay才能自动触发video的loadeddata事件,所以需要设置autoplay,曾经被坑哭了
可以了解更多video加载过程事件:http://www.w3school.com.cn/tags/av_event_loadeddata.asp - 在mounted内干这事
代码:
captureVideoImage() {
let videoArr = document.querySelectorAll(".video")
for(let i=0;i<videoArr.length;i++){
//loadeddata当当前帧的数据已加载,但没有足够的数据来播放指定音频/视频的下一帧时,会发生 loadeddata 事件。
//浏览器支持:所有主流浏览器都支持 loadeddata 事件。注释:Internet Explorer 8 或更早的浏览器不支持该事件。
let video = videoArr[i]
video.addEventListener('loadeddata', function () {
let canvas = document.createElement('canvas')
canvas.width = video.videoWidth //loadeddata事件后,获取视频宽高妥妥的
canvas.height = video.videoHeight
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
let dataUrl = canvas.toDataURL('image/png')
let pareantNode = video.parentNode
let imgNode = document.createElement('img')
imgNode.src = dataUrl
if( video.videoWidth > video.videoHeight ){
imgNode.style.height = '100%'
}else {
imgNode.style.width = '100%'
}
pareantNode.appendChild(imgNode)
video.pause()
}, false)
}
}
问题4:canvas绘制视频封面时,若视频非本地地址而是网络请求回来的地址,使用toDataURL会报安全问题错误
https://stackoverflow.com/questions/20424279/canvas-todataurl-securityerror
解决:设置video的标签属性crossorigin为anonymous,然后在视频加载后处理canvas。其中还需要后端配合设置CORS为所有*
video.setAttribute('crossorigin', 'anonymous')
问题5:ios设备上video标签不自动加载
由于浏览器的限制,用户未点击之前呈现的是黑屏,即使设置了preload="auto"也无效,体验很糟糕。需要配合设置video的poster属性,手动设置视频封面。
video.setAttribute('poster', dataUrl);
解决思路:canvas获取视频的第一帧,赋值给poster属性
过程中遇到一个很坑很坑的事情:虽然你设置了autoplay在Safari视频也不会自动播放,但是一定要设置autoplay才能触发video的loadeddata事件,就是这里被坑哭的,呜呜呜。。。
问题6:video标签在ios设备上自动全屏播放问题
到此,似乎已经很满意很开心了,可是在ios设备上查看视频时,鸡肋的将视频自动全屏播放,不是我想要的样子,打死都不开心,找找找,人家是可以设置的,Down there:
https://stackoverflow.com/questions/22697311/webkit-playsinline-on-iphone
<video :src="item" class="swipe-video" v-else controls="controls" webkit-playsinline playsinline x5-playsinline x-webkit-airplay="allow"></video>
终于相对满意了,开心,撒花花
最后附一下关键代码
data:
feedbackImages:['../static/images/test/img_1.jpg','../static/images/test/img_2.jpg','../static/images/test/img_3.png','../static/images/test/img_4.png','../static/images/test/video_1.mp4','http://www.source.com/video_2.mp4']
html:
<span v-for="(item,index) in feedbackImages" :key="item" @click.stop.prevent="view(index)" >
<img :src="item" v-if="isImg(item)" class="img"/>
<div class="video-con" v-else >
<video :src="item" class="swipe-video" x-webkit-airplay="allow" autoplay preload="auto" webkit-playsinline playsinline x5-playsinline x-webkit-airplay="allow"></video>
</div>
</span>
获取视频封面作为缩略图:
captureVideoImage() {
let videoArr = document.querySelectorAll(".video")
for(let i=0;i<videoArr.length;i++){
//loadeddata当当前帧的数据已加载,但没有足够的数据来播放指定音频/视频的下一帧时,会发生 loadeddata 事件。
//浏览器支持:所有主流浏览器都支持 loadeddata 事件。注释:Internet Explorer 8 或更早的浏览器不支持该事件。
let video = videoArr[i]
video.addEventListener('loadeddata', function () {
let canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
video.setAttribute('crossorigin', 'anonymous')//跨域设置,后端也需要设置CORS为*
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
let dataUrl = canvas.toDataURL('image/png')
let pareantNode = video.parentNode
let imgNode = document.createElement('img')
imgNode.src = dataUrl
if( video.videoWidth > video.videoHeight ){
imgNode.style.height = '100%'
}else {
imgNode.style.width = '100%'
}
pareantNode.appendChild(imgNode)
video.pause()
}, false)
}
}
设置video封面海报poster:
setPoster() {
let videoArr = document.querySelectorAll(".swipe-video")
for(let i=0;i<videoArr.length;i++){
let video = videoArr[i]
video.setAttribute('crossorigin', 'anonymous')
let canvas = document.createElement('canvas')
canvas.width =video.videoWidth
canvas.height =video.videoHeight
video.addEventListener('loadeddata', function () {
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
let dataUrl = canvas.toDataURL('image/png')
video.setAttribute('poster', dataUrl);
video.pause()
}, false)
}
}