在uniapp中使用了某些前端库或iframe,需要操作这些库中的dom的时候, 而uni上又没有document等基础对象。也就无法操作这些dom去实现一些交互逻辑,那么,涉及到这些的前端类库就无法使用,例如html2、canvas、image、video。而要用这些怎么办,这是用就出现了renderjs这种视图层工具来进行渲染。大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
使用方法:
- 在原有script下方再添加script,其中lang="renderjs"固定, module=“demo”,module的名字任意起
- 可以通过 this.$ownerInstance 获取当前组件的 ComponentDescriptor 实例。类似于vm
- 视图层绑定事件通过 module名称 . 逻辑层定义方法,有两个参数,1.事件对象event,2. 当前组件实例ComponentDescriptor
- 两个script间的通信需要通过:this.$ownerInstance 全局组件实例 或者 事件参数ComponentDescriptor 身上的callMethod方法,去抛出方法、传值,类似于vue组件间emit
- 可以使用 vue 组件的生命周期不可以使用 App、Page 的生命周期
使用参考:https://blog.csdn.net/dabaooooq/article/details/129272111
代码
uniapp在APP端video层级最高,不能被其它覆盖,引入videojs实现,使用renderjs实现原生的DOM操作。
<template><div class="full_screen_video" v-show="visible" :class="{ 'normal': !fullScreen }"><!-- 遮盖video 添加点击事件 --><div class="video_mask" @click="foldShortVideo" ></div><p v-if="fullScreen" class="back" @click="closeDialogHandle"><image src="@i/common/back_white.svg" alt=""></image><span>{{ title}}</span></p><view :info="videoItem" :change:info="videos.updateVideo" :options="videoOptions" :change:options="videos.optionsChange"class="video_box"ref="videoEle" id="videoEle"></view><view class="video_control_box" v-if="fullScreen" ><!-- 开始结束时间 --><view class="video_time"><span>{{ formatTime(videoCurrentTime) }} </span> <span>{{ formatTime(videoTotalTime) }}</span></view><!-- 视频播放器 --><div class="video_control"><p class="disp_ac"><image v-if="!isStart" src="@i/video/icon_video_start.svg" mode="widthFix" @tap="startAndStop('start')" alt=""></image><image v-else src="@i/video/icon_video_stop.svg" mode="widthFix" @tap="startAndStop('stop')" alt=""></image><image src="@i/video/icon_video_next.svg" mode="widthFix" @tap="playNext" alt="" v-if="videoList.length > 0"></image></p><p><view :class="{'normal_mode':normalMode}" class="formation_select_box mr60" @click="showOption = !showOption"><span class="select_text">{{ speedValue +'x'}}</span><view class="select_menu" :class="{ ani: showOption }" v-show="showOption"><view class="select_menu_item" v-for="item in speedOptions" :key="item.value"><span :class="{'active': speedValue === item.value}" @click="changeSpeed(item.value)">{{ item.label +'x'}}</span></view></view></view><image v-if="normalMode" src="@i/video/full_screen.svg" mode="widthFix" @tap="fullScreenVideo" alt=""></image></p></div></view><view v-else class="video_control_box video_control_box_small" ><div class="time_bar_box"><div class="time_btn"><image v-if="!isStart" src="@i/video/icon_video_start.svg" mode="widthFix" @tap="startAndStop('start')" alt=""></image><image v-else src="@i/video/icon_video_stop.svg" mode="widthFix" @tap="startAndStop('stop')" alt=""></image><image src="@i/video/icon_video_next.svg" mode="widthFix" @tap="playNext" alt="" v-if="videoList.length > 0"></image></div><div class="time_bar"><span>{{ formatTime(videoCurrentTime) }} </span> <span>{{ formatTime(videoTotalTime) }}</span></div></div><div class="video_btn"><view class="formation_select_box mr50" @click="showOption = !showOption"><span class="select_text">{{ speedValue +'x'}}</span><view class="select_menu" :class="{ ani: showOption }" v-show="showOption"><view class="select_menu_item" v-for="item in speedOptions" :key="item.value"><span :class="{'active': speedValue === item.value}" @click="changeSpeed(item.value)">{{ item.label +'x'}}</span></view></view></view> <image src="@i/video/download.svg" mode="widthFix" @tap="downloadVideo" alt=""></image><image v-if="normalMode" src="@i/video/full_screen.png" mode="widthFix" @tap="fullScreenVideo" alt=""></image></div></view><!-- 视频列表 --><div class="video_list" v-if="showVideoList && videoList.length" ><scroll-view scroll-x="true" ><div class="video_list_item"><div v-for="(item, index) in videoList" :key="index" class="video_item" :class="{'active': index === currentVideoIndex}" @click="playVideo(item, index)"><div class="video_img"><image class="snapshot" :src="item.snapshot && !item.imgError ? item.snapshot : defaultSnapshot" @error="item.imgError = true" alt="snapshot" ></image><span class="total_time" v-if="item.end && item.start">{{formatTime(item.end - item.start)}}</span></div><div class="clip_info"><div class="text_over">{{item.name || item.label || ''}}</div><div class="video_list_time">{{ formatDate(item.createTime).split('-').slice(0,3).join('/').split(' ').slice(0,1).join() }}</div></div></div></div></scroll-view></div></div>
</template><script>
export default {name: 'pad-video-play', // 在主视频内播放的组件,短片会跳转到主视频对应的时间props: {// 控制显示隐藏visible: {type: Boolean,default: false},// 主视频下的短视频播放列表videoList: {type: Array,default:() => []},// 主视频播放源videoMain: {type: Object,default: () => { }},// 视频类型, 默认为全屏播放videoType: {type: String,default: 'full_screen'},},data() {return {videoCurrentTime: 0, // 视频当前时间videoTotalTime: 0, // 视频总时长isStart: false, // 是否开始volumeValue: 1, // 音量初始值(0-1)showVideoList: true, // 是否显示视频列表currentVideoIndex: -1, // 当前播放的视频下标defaultSnapshot: require("static/images/video/default_snapshot.svg"), // 默认缩略图videoOptions: {closeDialog: false}, // 视频操作选项fullScreen: this.videoType === 'full_screen', // 是否是全屏speedValue: '1.0', // 初始播放速度showOption:false, // 控制倍速弹窗speedOptions: [ // 阵型选项{ label: '2.0', labelEn: '2.0', value: '2.0' },{ label: '1.5', labelEn: '1.5', value: '1.5' },{ label: '1.25', labelEn: '1.25', value: '1.25' },{ label: '1.0', labelEn: '1.0', value: '1.0' },{ label: '0.5', labelEn: '0.5', value: '0.5' },],videoItem: {},title:''};},computed: {// 是否是正常模式下的视频播放normalMode() {let boolean = this.videoType === 'normal';return boolean;}},watch: {currentVideoIndex(newVal) {this.$emit('change-video-index', newVal);},videoMain: {handler (newVal) { this.videoItem = {url: newVal.url}this.title = newVal.name },immediate: true,deep:true},visible(newVal) {if (newVal) { this.videoOptions = {closeDialog: false,volumeChangeOption: { volume: 1 } }this.volumeValue = 1;}}},methods: {foldShortVideo(){this.showVideoList = !this.showVideoList},// 关闭弹框closeDialogHandle () {this.videoOptions = {closeDialog: true}if (this.videoType === 'full_screen') {// setTimeout(() => { this.$emit('cancelVideo', false);// },100)} else {this.fullScreen = false;}this.showVideoList = false;this.$emit('update:visible',false)},// 开始视频onPlayerPlay() {this.speedValue = '1.0'this.isStart = true;},// 实时更新onTimeUpdate(currentTime){this.videoCurrentTime = currentTime;},// 暂停视频onPlayerPause() {this.isStart = false;},// 加载视频源数据onLoadedmetadata(totalTime) {this.videoTotalTime = totalTime;},// 开始或暂停视频startAndStop(type) {this.videoOptions = {startAndStopOption: { type }}},// 改变视频音量volumeChange(volume) {this.volumeValue = volume;this.videoOptions = {volumeChangeOption: { volume } }},// 播放视频playVideo(item, index) {this.speedValue = '1.0'// // 在正常模式播放下, 不可重复点击当前视频if (this.normalMode && item && this.currentVideoIndex === index) {return;}this.$set(this.videoItem, 'startTime', item.start);this.currentVideoIndex = index;this.videoList.forEach(item => {item.forceUpdate = false})item.forceUpdate = true},// 改变视频的速度changeSpeed(speed) {this.speedValue = speed || '1.0';this.videoOptions = {playbackRate: { speed }}},// 播放下一个playNext(){this.speedValue = '1.0'this.videoList.forEach(item => {item.forceUpdate = false})let index = this.currentVideoIndex;if(this.currentVideoIndex+1<this.videoList.length){ this.currentVideoIndex = index+1;this.$set(this.videoList[index+1],'forceUpdate',true)this.$set(this.videoList, index + 1, this.videoList[index + 1]);this.$set(this.videoItem, 'startTime', this.videoList[index+1].start);}else{ this.currentVideoIndex = 0;this.$set(this.videoList[0],'forceUpdate',true)this.$set(this.videoList, 0, this.videoList[0]);this.$set(this.videoItem, 'startTime', this.videoList[0].start);}},// 进入/退出 全屏视频fullScreenVideo() {this.fullScreen = !this.fullScreen;},// 格式化时间formatTime(result) {let h = Math.floor(result / 3600) < 10 ? "0" + Math.floor(result / 3600) : Math.floor(result / 3600);let m = Math.floor((result / 60) % 60) < 10 ? "0" + Math.floor((result / 60) % 60) : Math.floor((result / 60) % 60);let s = Math.floor(result % 60) < 10 ? "0" + Math.floor(result % 60) : Math.floor(result % 60);return Math.floor(result / 3600) == 0 ? m + ":" + s : h + ":" + m + ":" + s;},// 格式化日期formatDate(createTime){let date = new Date(createTime);let str = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;return str;}}
};
</script><script module="videos" lang="renderjs">import './video-offset.js'; // videojs 引入import Videojs from 'video.js';import 'video.js/dist/video-js.css';export default {data() {return {videoPlayer: null, // 当前视频播放器}},mounted() {},methods: {// 初始化视频initVideoJS(item) {if(this.videoPlayer){this.videoPlayer.dispose();this.videoPlayer = null;}let that = this;let videoEle = document.createElement('video');videoEle.style = 'width:100%; height:100%';videoEle.setAttribute("class", "video-js vjs-big-play-centered");let videos = document.getElementById('videoEle');console.log(videos);videos.appendChild(videoEle)let option = {controls: true, // 是否显示控制条preload: 'auto', // 是否预加载视频数据muted: false, // 是否静音language: 'zh-CN', // 设置语言autoplay: true, // 自动播放, 正常模式下不进行自动播放sources: [ // 视频源{type: "video/mp4",src: item.url}, {type: "video/webm",// webm格式src: item.url}, {type: "video/mov", // mov格式src: item.url},],controlBar: { // 设置控制条组件children: [{ name: 'progressControl' }, // 播放进度条// // { name: 'fullscreenToggle' } // 全屏按钮]}};// video.js初始化实例化的对象this.videoPlayer = Videojs(videoEle, option, function onPlayerReady() {// 开始视频this.on('play', function() {that.$ownerInstance.callMethod('onPlayerPlay');})// 暂停视频this.on('pause', function() {that.$ownerInstance.callMethod('onPlayerPause');})// 实时更新this.on('timeupdate', function() {let currentTime = that.videoPlayer ? that.videoPlayer.currentTime() : 0;that.$ownerInstance.callMethod('onTimeUpdate', currentTime);})// 加载视频源数据this.on('loadedmetadata', function() {let totalTime = parseInt(that.videoPlayer.duration()) || 0;that.$ownerInstance.callMethod('onLoadedmetadata', totalTime);})});},// 监听 videoData 数据变更updateVideo(newValue) {if(!newValue || !newValue.url) return;if(this.videoPlayer && newValue.startTime){this.videoPlayer.currentTime(newValue.startTime)return}this.initVideoJS(newValue);},// 监听改变视频操作选项optionsChange(newValue) {if (newValue && newValue.startAndStopOption) {// 开始或暂停视频let { type } = newValue.startAndStopOption;type === 'start' ? this.videoPlayer.play() : this.videoPlayer.pause();} else if (newValue && newValue.volumeChangeOption) {// 改变视频音量let { volume } = newValue.volumeChangeOption;this.videoPlayer && this.videoPlayer.volume(volume);} else if (newValue && newValue.closeDialog) {console.log('销毁了')// 销毁videoJs实例this.videoPlayer && this.videoPlayer.dispose();this.videoPlayer = null} else if (newValue && newValue.continuePlay) {// 继续播放视频let { url } = newValue.continuePlay;this.initVideoJS(url);} else if(newValue && newValue.playbackRate){let { speed } = newValue.playbackRate;// 改变视频的播放速度this.videoPlayer && this.videoPlayer.playbackRate(speed);}}}}
</script><style lang='less' scoped>
.video_mask {position: absolute;width: 100%;height: calc(100% - 62.81rpx);top: 0;left: 0;background-color: transparent;z-index: 1;
}
::v-deep {// 视频style.video_box{width: 100%;height: 100%;video{object-fit:fill;}}.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{opacity: 1;}.video-js{position: relative;.vjs-control-bar {width:663.32rpx;left: 43.34rpx;bottom: 47.9rpx;background-color: transparent;z-index: 105;opacity: 1;}.vjs-play-progress{background: #38CB89;&::before{color: #38CB89;}}.vjs-slider{background-color: rgba(255, 255, 255, 0.36);}}
}
.disp_ac{display: flex;align-items: center;
}
/* 文字超出部分设置为... */
.text_over {max-width: 85%;word-break: break-all;text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2; /* 这里是超出几行省略 */overflow: hidden;
}
.full_screen_video{width: 100%;height: 100%;position: fixed;z-index: 102;left: 0;top: 0;box-sizing: border-box;color: #fff;font-family: 'SysFontR';// 默认视频样式&.normal{width: 100%;height: 100%;position: relative;.video_list,.video_shrink{display: none;}.video_control_box{width: 100%;height: 37.69rpx;position: absolute;left: 0;bottom: 4.71rpx;z-index: 5;display: flex;flex-direction: column;align-items: center;padding: 0 10.05rpx;box-sizing: border-box;&.video_control_box_small{.formation_select_box{bottom: 7rpx;.select_text{font-size: 10.05rpx;}.select_menu{bottom:0;transform:scale(.8) }}}.time_bar_box{width: 100%;height: 12.25rpx;display: flex;align-items: center;.time_btn{image{width: 11.93rpx;height: 9.42rpx;margin-right: 10.05rpx;}}.time_bar{flex: 1;display: flex;justify-content:space-between;font-size: 9.74rpx;color: #f3f3f3;}}.video_btn{width: 100%;display: flex;justify-content: flex-end;text-align: right;margin-top: 6.28rpx;image{width: 10.99rpx;height: 10.05rpx;margin-right: 15.7rpx;}}}.video_control{height: 47.11rpx;bottom: 4.71rpx;image{width: 10.05rpx !important;height: 10.05rpx !important;margin-right: 15.7rpx;}}::v-deep {.vjs-control-bar{width: calc(100% - 74.75rpx - 37.69rpx);left: 76.75rpx;bottom: 27.84rpx;}}}.back{position: absolute;z-index: 200;top: 0;left: 0;width: 100%;height: 58.59rpx;padding-left: 14.64rpx;font-size: 13.82rpx;display: flex;align-items: center;cursor: pointer;background: linear-gradient(180deg, #000000 0%, rgba(0, 0, 0, 0) 100%);image{width: 6.44rpx;height: 12.3rpx;margin-right: 20.1rpx;}}.formation_select_box{position: relative;border-radius: 3.77rpx;display: flex;align-items: center;justify-content: center;&.normal_mode{right: 118rpx;}.select_text{font-family: 'AkrobatMedium';font-size: 11.31rpx;color: #fff;position: relative;}.select_menu {width: 43.66rpx;position: absolute;bottom: 18.04rpx;left: -13rpx;z-index: 999;padding: 13.82rpx 0 0;text-align: center;box-sizing: border-box;border-radius: 3.77rpx;background: #000000;color: #F3F3F3;.select_menu_item {position: relative;z-index: 2;margin-bottom: 12.56rpx;>span {display: inline-block;width: 100%;height: 100%;box-sizing: border-box;font-size: 10.05rpx;font-family: AkrobatRegular;&.active{color: #38CB89;}}}}.ani {animation: ani 0.2s;}@keyframes ani {0% {transform: scaleY(0);}100% {transform: scaleY(1);}}.mask_box {width: 70vw;height: 100vh;position: fixed;left: 0;top: 60rpx;z-index: 99;background: transparent;}}.video_time{position: absolute;bottom: 49.94rpx;width: 100%;box-sizing: border-box;padding:0 15.7rpx;line-height: 14.13rpx;display: flex;justify-content:space-between;z-index: 104;}.video_control{position: absolute;left: 0;z-index: 5;bottom: 5.85rpx;width: 161.13rpx;height: 35.15rpx;display: flex;align-items: center;justify-content: space-between;padding: 0 19.04rpx;box-sizing: border-box;width: 100%;p{display:flex;align-items: center;image{width: 17.59rpx;height: 17.59rpx;margin-right: 15.23rpx;// opacity: .6;}.video_volume{position: relative;.video_volume_slider{display: none;position: absolute;right: 30px;top: -85px;padding-top: 5px;background-color: rgba(0, 0, 0, 0.36);&.hover{display: inline-block;}}}}}.video_list{position: absolute;width: 100%;top: 0;right: 0;z-index: 201;background: rgba(29, 29, 29, 0.8);backdrop-filter: blur(50px);color: #fff;padding-top: 25.13rpx;padding-bottom: 6.28rpx;box-sizing: border-box;display: flex;// transition: all 200ms;.close_img{position: absolute;top: 50%;transform: translateY(-50%);right: 236.81rpx;width: 20.5rpx;height: 75rpx;}.video_item{width: 83.54rpx;flex-shrink: 0;display: flex;flex-direction: column;margin-left: 12.56rpx;box-sizing: border-box;display: flex;&.active{.text_over{font-family: SysFontM;color: #38CB89;}}.video_img{width: 100%;height: 47.11rpx;box-sizing: border-box;position: relative;image.snapshot{border-radius: 3.77rpx;width: 100%;height: 100%;}.total_time{font-family: SysFontR;height: 12.89rpx;position: absolute;left: 2.92rpx;bottom: 2.92rpx;padding: 0 5.85rpx;font-size: 8.2rpx;line-height: 12.89rpx;color: rgb(255, 255, 255);background: rgba(30, 30, 30, .6);}}.clip_info{width: 100%;box-sizing: border-box;font-size: 8.2rpx;color: rgba(255, 255, 255, .8);position: relative;padding: 4.71rpx 0;// .clip_label{// font-size: 10.68rpx;// color: rgba(255, 255, 255,1);// line-height: 14.06rpx;// margin-bottom: 7.54rpx;// display: flex;// align-items: center;// }}}}.video_list_item {display: flex;width: 100%;overflow-x: auto;}.video_shrink{position: fixed;top: 50%;transform: translateY(-50%);right: 0;width: 20.5rpx;height: 75rpx;}.video_list_time {font-family: AkrobatRegular;margin-top: 3px;}
}
</style>;