当前位置: 首页 > article >正文

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>


http://www.kler.cn/a/452987.html

相关文章:

  • STM32学习(一)
  • 拉普拉斯分布极大似然估计
  • es快速扫描
  • sentinel限流+其他
  • 4.3 数据库HAVING语句
  • 2024最新鸿蒙开发面试题合集(一)-HarmonyOS NEXT Release(API 12 Release)
  • Amazon Bedrock 上线 Stable Diffusion 3.5 Large模型,助力高质量图像生成
  • cs-script:一个非常成熟的C#脚本开源引擎
  • Zero Trust 模型:重新定义数字化时代的安全策略
  • docker 释放磁盘空间--常用清理命令
  • Mac 查询IP配置,网络代理
  • C语言版解法力扣题:将整数按权重排序
  • mysql 查询优化之字段建立全文索引
  • 使用Python实现量子计算应用:走进量子世界的大门
  • 常用滤波算法之中位值滤波算法
  • Artec Space Spider助力剑桥研究团队解码古代社会合作【沪敖3D】
  • centos server系统新装后的网络配置
  • 安卓 自定义矢量图片控件 - 支持属性修改矢量图路径颜色
  • Go入门篇:(一)golang的安装和编辑工具安装
  • MySQL深度解析:高效查询优化与实战案例
  • 【JAVA高级篇教学】第四篇:MySQL 5.7 与 MySQL 8 的区别
  • 【LeetCode】394、字符串解码
  • python怎么看矩阵维数
  • 低代码配置式组态软件-BY组态
  • 深入理解 MySQL 索引
  • 数据流动背后的“暗流涌动”,企业如何借助多源威胁检测响应高效捕捉安全威胁?