记录一下在uni-app我的项目中遇到的一些问题。
一、抉择视频并上传,播放
展现:
全屏播放:
1、<video>在非H5端图层太高的问题
安卓APP中,因为<video>是原生组件,层级高于一般前端组件,笼罩其须要应用<cover-view>、<cover-image>,但要在<video>开始完结标签之间。
自定义播放按钮,删除按钮都能够应用这些标签实现。
但展现抉择的视频时,弹出抉择框(如picker),就无奈用这些标签实现层级高于<video>。
实现形式:展现时用view,播放时用<video>全屏播放。
html局部
<!-- 展现视频 --><!-- #ifdef APP-PLUS || H5 --><view :id="`${item.id}`" class="video"> <image class="video-poster" :src="item.posterUrl"/> <image v-if="clearable" class="video-clear-btn" src="/static/clear.png" @click="deleteFile(item, index)"/> <image class="video-play-btn" src="/static/play.png" @click="toPlayVideo(item.url)"/></view><!-- #endif --><!-- 全屏播放视频 --><video class="full-play-video" id="fullScreenVideo" v-if="videoUrl" :src="videoUrl" object-fit="contain" @fullscreenchange="fullscreenchange" @error="onVideoError"/>
js局部
mounted() { // #ifdef APP-PLUS || H5 this.videoContext = uni.createVideoContext('fullScreenVideo') // #endif // #ifdef MP-WEIXIN this.videoContext = uni.createVideoContext('fullScreenVideo', this) // #endif }, beforeDestroy() { clearTimeout(this.timer) this.timer = null }, methods: { toPlayVideo(url) { this.videoUrl = url //.split('?')[0] this.timer = setTimeout(() =>{ // #ifdef APP-PLUS || H5 this.videoContext = uni.createVideoContext('fullScreenVideo') // #endif // #ifdef MP-WEIXIN this.videoContext = uni.createVideoContext('fullScreenVideo', this) // #endif // 微信开发工具不失效,要真机测试 this.videoContext.requestFullScreen({ direction: 0 }) this.videoContext.play() }, 100) }, fullscreenchange(e) { if(!e.detail.fullScreen) { this.videoContext.stop() this.videoUrl = false } }, onVideoError(err) { console.log('播放视频失败:', err) }, deleteFile(file, index) { uni.showModal({ title: '提醒', content: '您确定要删除此项吗?', success: res => { if (res.confirm) { this.$emit('delete-item', { file, index }) } } }) }, }
2、获取视频的封面
展现视频的view外面,要用一个图片作为视频的封面,能够用视频的首帧。
获取视频的封面,用到renderjs,通过canvas实现。
html局部
<!-- #ifdef APP-VUE || H5 --><view :prop="videos" :change:prop="rdVideo.getAllPoster" style="opacity: 0;"></view><!-- #endif -->
js局部
<!-- #ifdef APP-VUE || H5 --><script module="rdVideo" lang="renderjs"> import mixinVideo from './video-poster.js' // 监听videos, 有更改调用rdVideo.getAllPoster办法获取视频封面 export default { mixins: [mixinVideo] } </script><script>export default { data() { return { videos: [] } } methods: { // 获取视频封面后,调用此办法更新视频数据 updateVideos(newVal) { this.videos = newVal }, }}</script><!-- #endif -->
video-poster.js
const mixinVideo = { methods: { getVideoPoster(url) { return new Promise(function (resolve, reject) { let video = document.createElement('video') video.setAttribute('crossOrigin', 'anonymous') // 解决跨域,H5需后盾反对,申请的视频资源响应招标需有Access-Control-Allow-Origin video.setAttribute('src', url) video.setAttribute('width', 400) video.setAttribute('height', 400) video.setAttribute('preload', 'auto') // uni.chooseVideo抉择视频,入选用手机拍摄的视频时,地址是绝对地址,如 _doc/uniapp_temp_1650594368317/camera/1650594390147.mp4 // 可播放,然而loadeddata始终不执行,会触发error事件,视频加载失败 // 因先转换老本地地址 video.addEventListener('loadeddata', function () { console.log('视频第一帧加载完') let 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 const dataURL = canvas.toDataURL('image/jpeg') // 转换为base64 console.log('getVideoPoster-dataURL', dataURL.slice(0, 16)) resolve(dataURL) }) video.addEventListener('error', err => { console.log('视频加载失败', err) reject(err) }) }) }, async getAllPoster(newVal, oldVal, owner, instance) { console.log('执行getAllPoster') // renderjs中,监听的属性videos是一个数组,寄存的是选取的视频信息 // 删除,或updateVideos更新后(长度一样) if(newVal.length <= oldVal.length) return // 有默认值或增加时 const newList = [] for(const item of newVal) { // 已获取视频封面的不再反复获取 if(item.posterUrl !== undefined) { newList.push({ ...item }) continue } try { let url = item.url // 拍摄视频:_doc/uniapp_temp_1650594368317/camera/1650594390147.mp4 // 网络视频:https:// // 本地视频:file:// if(!item.url.includes('file://') && !item.url.includes('https://')) { // 将本地URL门路转换成平台绝对路径 // 如输出url为“_doc/a.png”: // Android平台转换后的门路为“/storage/sdcard0/Android/data/io.dcloud.HBuilder/.HBuilder/apps/HBuilder/doc/a.png”; // #ifdef APP-VUE url = 'file://' + plus.io.convertLocalFileSystemURL(item.url) // #endif } const dataUrl = await this.getVideoPoster(url) newList.push({ ...item, posterUrl: dataUrl }) } catch (err) { newList.push({ ...item }) } } this.$ownerInstance.callMethod('updateVideos', newList) } }} export default mixinVideo