uniapp 基于xgplayer(西瓜视频) + renderjs开发,实现APP视频播放
背景:在uniapp中因原生video组件功能有限,选择引入
xgplayer
库来展示视频播放等功能。并且APP端无法操作dom
,所以使用了renderjs
。
其他的不多说,主要列举一下renderjs中需要注意的点:
1、使用:在标签后,添加<script module="xgPlayerModule" lang="renderjs"></script >
,然后有关操作dom的代码都写在script 标签里面。其中module的值是自定义
的,后续会用这个值来调用renderjs的方法。
2、普通 script 标签是逻辑层
,带renderjs的 script 标签是视图层
。
3、逻辑层向视图层传值:在 template 某元素上添加 :属性名=“vue 变量”:change:属性名=“renderjs 模块名.方法名”
(其中 vue 变量 为 Vue 组件中的某个数据, renderjs 模块名 为之前定义的 module 属性的值,当 vue 变量 值改变时会触发 renderjs模块名.方法名 的方法)。例如:
<view :iosTimes="iosTimes" :change:iosTimes="xgPlayerModule.changeTimes"></view>
<script module="xgPlayerModule" lang="renderjs">
export default {
data(){
return {
renderIosTimes: null
}
},
methods: {
changeTimes() { // 当检测到iosTimes变化时,触发此方法,重新赋值后,保存在renderjs里面的renderIosTimes 变量中,因为后面renderjs里面的其他方法要使用这个值。
this.renderIosTimes = this.iosTimes
}
}
}
</script>
4、**视图层向逻辑层传参:**在 template 元素的事件绑定中使用 @事件=“renderjs 模块名.方法名”
,通过该方式触发 renderjs 中的方法,然后在这个方法中可以使用 this.$ownerinstance.callmethod('vue 组件中的方法名', 参数)
来调用普通 Vue 组件中的方法并传值。
this.$ownerInstance.callMethod('playerMethods')
接下来就是在renderjs中,通过xgplayer创建视频,并且完成交互
准备:
npm install xgplayer --save
以下是完整的代码案例:
video.vue中调用xgplayer组件
<xg-player :data-idkey="modId" ref="video" :videoConfig="videoConfig" ></xg-player>
import xgPlayer from '@/components/player/xgplayer';
<script>
export default {
data(){
return {
videoConfig: {
videoUrl: '', // 播放地址
lastPlayTime: '' // 上次播放时间
}
}
},
methods: {
changeTimes() { // 当检测到iosTimes变化时,触发此方法,重新赋值后,保存在renderjs里面的renderIosTimes 变量中,因为后面renderjs里面的其他方法要使用这个值。
this.renderIosTimes = this.iosTimes
}
},
onHide: function() { // 退出应用时调用
this.$refs['video'].beforeLeave() // 调用子组件的方法,暂停视频播放
}
}
</script>
xgplayer.vue组件中:
<template>
<view>
<!-- 视频 -->
<view
:id='videoId'
class="mse-box"
:style="customStyle"
v-if="type === 'VOD'"
:conEnd="conEnd" :change:conEnd="xgPlayerModule.initVideo"
></view>
<view :iosTimes="iosTimes" :change:iosTimes="xgPlayerModule.changeTimes"></view>
<view :modOpenTrial="modOpenTrial" :change:modOpenTrial="xgPlayerModule.changeModOpenTrial"></view>
<view :playOrPauseStatus="playOrPauseStatus" :change:playOrPauseStatus="xgPlayerModule.playOrPauseVideoRenderjs"></view>
</view>
</template>
<!-- 逻辑层 -->
<script>
export default {
name: 'xgPlayerHls',
data: function() {
return {
config: {
id: this.videoId,
url: '',
fluid: true,
playbackRate: [0.75, 1, 1.5, 2],
defaultPlaybackRate: 1,
definitionActive: 'click',
poster: '',
volume: 1,
autoplay: false, // 手动点击播放,节省用户流量
playsinline: true,
lastPlayTime: 0, //视频起播时间(单位:秒)
lastPlayTimeHideDelay: 3,
enableVideoDbltouch: true,
rotateFullscreen: true,
// rotate: {
// innerRotate: true,
// clockwise: false
// },
fitVideoSize: 'auto'
},
inFullscreen: false,
inLandscape: false,
conEnd: null,
playOrPauseStatus: 'pause'
};
},
components: {
},
computed: {
// h5通过监听屏幕角度去写入样式
customStyle() {
const that = this;
if (that.inFullscreen && that.inLandscape) {
return {
height: '100vh !important',
minWidth: '100%',
width: '100% !important',
left: '0%',
transform: 'rotate(0deg)'
};
} else if (that.inLandscape) {
return {
width: '100% !important',
height: '400rpx !important'
};
} else {
return {
width: '100%',
height: '400rpx'
};
}
}
},
props: {
rootStyle: {
type: Object,
default() {
return {};
}
},
iosTimes: {
type: Boolean,
default() {
return false;
}
},
videoConfig: {
type: Object,
default() {
return {videoUrl: '', lastPlayTime: 0};
}
},
modOpenTrial: {
type: Boolean,
default() {
return true;
}
},
modDrag: {
type: Boolean,
default() {
return true;
}
},
fastForward: {
type: Boolean,
default() {
return true;
}
},
type: {
type: String,
default() {
return 'VOD';
}
},
videoId: {
type: String,
default() {
return 'mse';
}
}
},
methods: {
beforeLeave() { // 离开页面暂停视频播放
this.playOrPauseStatus = 'pause'
},
playVideo() {
this.playOrPauseStatus = 'play'
},
pauseVideo() {
this.playOrPauseStatus = 'pause'
},
handleOrientation() {
const orientation = window.orientation;
console.log('orientation=当设备的方向发生变化时触发的事件===', orientation);
switch (orientation) {
case 0:
// 竖屏
this.inLandscape = false;
break;
case 90:
// 左横屏
this.inLandscape = true;
break;
case -90:
// 右横屏
this.inLandscape = true;
break;
case 180:
// 倒立
break;
default:
// 未知
break;
}
},
fullScreenMe() { // 进入全屏
this.inFullscreen = true;
this.$emit('on-goLandscape', 'ON');
},
exitFullScreenMe() { // 退出全屏
this.inFullscreen = false;
this.$emit('on-goLandscape', 'OFF');
},
playTimeUpdate(currentTime) { // timeupdate播放中
// console.log('播放中=============');
this.$emit('playerVideo', currentTime);
},
openTrialMe() {
// 弹框有问题
this.$dialog.alert({
width: '80%',
confirmButtonColor: '#00aeef',
confirmButtonText: this.$t('mall_10'),
message: this.$t('see_end')
});
},
playerMethods() { // 播放
console.log('播放============');
this.playOrPauseStatus = 'play'
this.$emit('videoPlay')
},
playerPauseMethods() { // 暂停
console.log('暂停============');
this.playOrPauseStatus = 'pause'
this.$emit('videoPause')
},
init() {
if (this.videoConfig.videoUrl) {
if (this.type === 'VOD') {
this.config.id = this.videoId;
this.config.lastPlayTime = this.videoConfig.lastPlayTime;
this.config.url = this.videoConfig.videoUrl;
this.config.autoplay =this.videoConfig.autoplay
this.config.ignores = this.fastForward ? [] : ['progress'];
if (this.videoConfig.duration) {
this.config.duration = this.videoConfig.duration;
}
// m3u8 格式播放
if (this.videoConfig.videoUrl.indexOf('m3u8') !== -1) {
this.config.preloadTime = 15; // 预加载时间15s
this.config.minCachedTime = 5; // 最小缓存时间
// iOS手机不兼容此项属性,故作此判断
// 使用 uni.getSystemInfoSync() 获取系统信息
const systemInfo = uni.getSystemInfoSync();
const platform = systemInfo.platform;
if (platform === 'android') {
this.config.useHls = true;
}
}
this.conEnd = this.config; // config赋值完成
}
}
}
},
mounted() {
// 是否有权限观看完整视频, true: 能 , false: 禁止拖拽; 限制 10%;
this.config.disableProgress = !this.modOpenTrial;
//如果禁止拖动,后台设置的false,前端设置值得是true.如果允许拖动,后台设置的true,前端设置值得是false.
this.config.allowSeekPlayed = !this.modDrag;
// #ifdef APP-PLUS
// 监听事件 当设备的方向发生变化时触发的事件
plus.globalEvent.addEventListener('orientationchange', this.handleOrientation);
// #endif
// #ifndef APP-PLUS
window.addEventListener('orientationchange', this.handleOrientation);
// #endif
this.init();
},
watch: {
modOpenTrial(val) {
// 是否有权限观看完整视频, true: 能 , false: 禁止拖拽; 限制 10%;
this.config.disableProgress = !this.val;
}
}
};
</script>
<!-- 视图层 -->
<script module="xgPlayerModule" lang="renderjs">
import xgPlayer from 'xgplayer';
import HlsJsPlayer from 'xgplayer-hls.js';
export default {
data(){
return {
player: null,
renderIosTimes: null,
renderModOpenTrial: null
}
},
mounted() {
},
beforeDestroy() {
this.player && typeof this.player.destroy === 'function' && this.player.destroy();
},
methods: {
playOrPauseVideoRenderjs() { // 控制视频播放和暂停
console.log('暂停视频 或者 播放', this.playOrPauseStatus);
if (this.player) {
if (this.playOrPauseStatus === 'play') {
this.player.play();
} else {
this.player.pause();
}
}
},
changeTimes() {
this.renderIosTimes = this.iosTimes
},
changeModOpenTrial() {
this.renderModOpenTrial = this.modOpenTrial
},
initVideo(newVal,old,ownerInstance,instance) {
let that = this
if (that.conEnd && that.conEnd.id && that.conEnd.url && !that.player) {
console.log('视图层的initVideo方法========');
if (that.player) {
that.player.destroy();
}
if (that.conEnd.url.indexOf('m3u8') !== -1) {
that.player = new HlsJsPlayer(that.conEnd);
} else { // 不是m3u8
that.player = new xgPlayer(that.conEnd);
if (that.renderIosTimes) {
if (that.player) {
that.player.start(that.conEnd.url);
that.player.play();
}
}
}
console.log('that.player=======', that.player);
// 播放
that.player.on('play', function () {
// 调用逻辑层方法
that.$ownerInstance.callMethod('playerMethods')
})
// 暂停
that.player.on('pause', function () {
// 调用逻辑层方法
that.$ownerInstance.callMethod('playerPauseMethods')
})
// 播放中
that.player.on('timeupdate', function(resPlayer) {
// 是否有权限观看完整视频, true: 能 , false: 禁止拖拽; 限制 10%;
if (!that.renderModOpenTrial && (resPlayer.currentTime / resPlayer.duration) >= 0.1) {
resPlayer.pause();
// resPlayer.currentTime = 0;
that.$ownerInstance.callMethod('openTrialMe')
}
// 调用逻辑层方法
that.$ownerInstance.callMethod('playTimeUpdate', resPlayer.currentTime)
});
// 进入全屏
that.player.on('getRotateFullscreen', function(value) {
if (document.getElementsByClassName('water-turn')[0]) {
document.getElementsByClassName('water-turn')[0].style.transform = 'rotate(90deg)';
}
that.$ownerInstance.callMethod('fullScreenMe')
});
// 退出全屏
that.player.on('exitRotateFullscreen', function(value) {
if (document.getElementsByClassName('water-turn')[0]) {
document.getElementsByClassName('water-turn')[0].style.transform = 'rotate(0deg)';
}
that.$ownerInstance.callMethod('exitFullScreenMe')
});
}
}
}
}
</script>