uniapp 解决app端视频滚动错误、首次加载黑屏的问题(包含后续的视频播放、下载)
我的理解:出现app端视频滚动错误的原因是因为<video/>
组件在非H5端是原生组件,层级高于普通前端组件。首次加载黑屏的问题,我暂时还不知道原因
我的解决方案:获取视频第一帧转成图片展示
1、后端处理
刚开始在网上搜索解决办法都是在将video组件更换为image组件,地址仍写video的地址,在地址后面加上'?x-oss-process=video/snapshot,t_0,f_jpg'
后端给我返回的地址是预览视频地址,我加上这个是没有反应的。我试着用uniapp官网的视频地址,发现加不加这段都可以。
<image :disabled="false" :controls='false' :autoplay='false' :src="item+'?x-oss-process=video/snapshot,t_0,f_jpg'" mode=""></image>
官方视频地址跟我的视频地址的差别就是访问他的会直接下载视频,访问我的只是预览视频。所以我怀疑有这个的原因,跟后端沟通过,他说改成下载地址很麻烦。
最后的解决方法是当我访问视频的链接后面加了'?x-oss-process=video/snapshot,t_0,f_jpg',他就给我返回图片
2、前端处理:前端封装 video,结合renderjs把视频的第一帧转成图片展示
父组件页面:
<template> <view class="content image" @tap="showView(filePath)"> <video-content :filePath="filePath"></video-content> </view> </template>
import VideoContent from './components/videoContent.vue'; export default { components: { VideoContent }, methods: { showView(filePath) { uni.navigateTo({ url: `/page/communication/components/videoView?filePath=${filePath}` }) } } }
video-content组件页面
<template> <view class="videoContent"> <!-- 播放按钮 --> <view class="video-cover"> <image class="play-button" src="/static/communication/pc-play.png"></image> </view> <!-- 逻辑层调用视图层方法,采用监听data中变量改变的方法 --> <view id="canvas" class="canvas" :prop="newVal" :change:prop="canvas.create"></view> <!-- 逻辑层生成的图片回显 --> <image class="image" :src="demo" mode=""></image> </view> </template>
<!-- 逻辑层script--> <script> export default { name: 'videoContent', props: { // 图片地址 filePath: { type: String } }, data() { return { demo: '', } }, computed: { newVal() { return this.filePath } }, onLoad() { }, methods: { getDataURL(options) { this.demo = options.dataURL } } } </script> <!-- 视图层script module对应HTML代码中view的id--> <script module="canvas" lang="renderjs"> export default { methods: { // 视图层创建base64图片 create(newValue, oldValue, ownerInstance){ // 第一次进入为空不操作 if(newValue == null){ return } let video = document.createElement('video') video.setAttribute('crossOrigin', 'anonymous') // 处理跨域,H5需后台支持,请求的视频资源响应投标需有Access-Control-Allow-Origin video.setAttribute('src', newValue) video.setAttribute('width', 200) video.setAttribute('height', 200) 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 // 传递数据给逻辑层 ownerInstance.callMethod('getDataURL',{ dataURL: dataURL }) console.log('getVideoPoster-dataURL', dataURL.slice(0, 16)) }) video.addEventListener('error', err => { console.log('视频加载失败', err) }) } } } </script>
<style lang="scss" scoped> .videoContent { position: relative; .video-cover { position: absolute; width: 100%; height: 100%; background-color: rgba(1, 1, 1, .5); z-index: 1; display: flex; align-items: center; justify-content: center; .play-button { width: 40rpx; height: 40rpx; background-size: 50%; background-repeat: no-repeat; background-position: 50% 50%; cursor: pointer; } } .image { max-width: 160rpx; max-height: 260rpx; } } </style>
3、 点击,跳转页面实现视频播放、视频保存到本地相册
<template> <transition name="slide-fade"> <view class="videoView"> <cover-image class="cover-video" src="/static/communication/download.png" @tap="download"></cover-image> <video id="popupVideo" :src="videoUrl" autoplay :show-fullscreen-btn="false" :show-center-play-btn="false"> </video> </view> </transition> </template> <script> import http from '@/api/request' import { toast } from '../../../utils' export default { data() { return { videoUrl: '' } }, onLoad({ filePath }) { this.videoUrl = http.config.fileUrl + filePath }, methods: { download() { console.log('this.videoUrl', this.videoUrl) uni.downloadFile({ url: this.videoUrl, success: (res) => { if (res.statusCode === 200) { uni.saveVideoToPhotosAlbum({ filePath: res.tempFilePath, success: function () { toast("保存成功"); }, fail: function () { tToast("保存失败,请稍后重试"); } }); } } }); } } } </script> <style lang="scss" scoped> .videoView { video { margin-top: 72rpx; height: calc(100% - 72rpx); width: 100%; } .close { padding: 12rpx; position: absolute; top: 0; right: 0; image { width: 48rpx; height: 48rpx; } } .cover-video { display: block; position: fixed; right: 15rpx; bottom: 15rpx; width: 70rpx; height: 70rpx; z-index: 999; } } </style>