# Video Player 组件播放流程文档 ## 概述 `video-player` 是一个功能完整的Vue视频播放器组件,支持自定义控制栏、全屏播放、倍速播放、进度控制等功能,专为移动端优化。 ## 组件结构 ### 核心依赖 - `progress-bar.vue` - 自定义进度条组件 - `vuex` - 状态管理,获取用户信息 - `study.js` - 学习工具类 - `manage.js` - 管理接口 ### 主要Props - `url` (String, required) - 视频播放地址 - `blobId` (String) - 视频内容ID,用于进度记录 - `watermark` (Boolean, default: true) - 是否显示水印 - `name` (String) - 视频名称 - `drag` (Boolean, default: true) - 是否允许拖拽进度条 - `inittime` (Number, default: 0) - 初始播放位置(秒) ## 视频播放流程详解 ### 1. 组件初始化流程 #### 1.1 挂载阶段 (mounted) ```javascript mounted() { // 1. 设置初始播放时间 this.initPlayingTime = this.inittime; // 2. 创建视频上下文对象 this.videoContext = uni.createVideoContext("xvideoPlayer", this); // 3. 获取系统信息(屏幕尺寸) uni.getSystemInfo({ success: function (info) { _this.videoPageWidth = info.windowWidth; _this.videoPageHeight = info.windowHeight; } }); // 4. 检查移动端兼容性 if (this.xvideoBool) this.dictCode(); // 5. 启动定时器更新进度 setInterval(() => { // 更新iOS播放时间 if (this.playing && !this.isMousedownProgress) { this.iosCurrentTime = this.iosCurrentTime + 1 } // 更新进度文字显示 this.updateProgressText(); }, 1000); // 6. 绑定全局事件监听器 window.addEventListener("touchstart", (e) => { if (this.speedListShow && !e.target.closest('.speed-control')) { this.speedListShow = false } }, {passive: false}); } ``` #### 1.2 兼容性检测 ```javascript function agentFullScreen() { let bool = false let agent = navigator.userAgent if (agent.indexOf('XiaoMi') != -1) { bool = true } return bool } ``` ### 2. 视频加载流程 #### 2.1 元数据加载 (onMetaLoad) ```javascript onMetaLoad(e) { // 1. 获取视频总时长 this.curVideo.duration = Number(e.detail.duration); // 2. 格式化并显示总时长 this.druationTime = this.formatSeconds(this.curVideo.duration); // 3. 显示控制栏 this.contrlShow = true; // 4. 触发父组件事件 this.$emit('loadedmetadata', e); } ``` #### 2.2 视频数据加载 (onLoad) ```javascript onLoad(e) { // 隐藏控制栏 this.contrlShow = false; // 触发父组件事件 this.$emit('loadeddata', e); } ``` ### 3. 播放控制流程 #### 3.1 播放/暂停切换 ```javascript videoOpreation() { // 根据当前状态切换播放/暂停 this.playing ? this.videoContext.pause() : this.videoContext.play(); this.playing = !this.playing; } ``` #### 3.2 播放开始 (onPlay) ```javascript onPlay() { // 1. 开始播放 this.videoContext.play(); this.playing = true; // 2. 设置控制栏自动隐藏 if (this.contrlShow) { this.contrlShowTimeout = window.setTimeout(function () { $this.contrlShow = false; }, 5000); } // 3. 触发父组件播放事件 this.$emit('play') } ``` #### 3.3 播放暂停 (onPause) ```javascript onPause() { this.playing = false; this.$emit('pause') } ``` #### 3.4 播放结束 (onEnded) ```javascript onEnded() { // 1. 停止播放 this.playing = false; // 2. 退出全屏(如果在全屏状态) if (this.fullScreenFlag) { this.fullScreen() } // 3. 触发父组件结束事件 this.$emit('ended') } ``` ### 4. 进度控制流程 #### 4.1 进度更新 (onTimeUpdate) ```javascript onTimeUpdate(e) { // 1. 更新播放时间计数器 this.currtimeText = this.currtimeText + 1 // 2. 处理进度更新 this.videoUpdate(e); // 3. 触发父组件时间更新事件 this.$emit('timeupdate', e) } ``` #### 4.2 详细进度处理 (videoUpdate) ```javascript videoUpdate(e) { // 1. 记录历史时间 this.historyTime = e.detail.currentTime // 2. 计算进度条数值 let duration = this.curVideo.duration; let sliderValue = e.detail.currentTime / duration * 100; let second = sliderValue / 100 * duration; // 3. 更新进度条(仅在非拖拽状态) if (this.updateState) { this.sliderValue = sliderValue; } // 4. 更新时间显示 this.druationTime = this.formatSeconds(duration); this.currtime = this.formatSeconds(second); this.iosCurrentTime = second; // 5. 保存播放进度(禁止拖拽模式下) if (!this.drag) { var time = localStorage.getItem('videoProgressData') var arr = time && JSON.parse(time) || {} if (arr[this.blobId] < this.sliderValue / 100 || !arr[this.blobId]) { arr[this.blobId] = parseFloat((this.sliderValue / 100).toFixed(8)) if (arr[this.blobId]) localStorage.setItem('videoProgressData', JSON.stringify(arr)) } } } ``` #### 4.3 进度条拖拽控制 ```javascript // 点击进度条更新播放进度 updateProgressByClickBar(type, value) { let duration = this.curVideo.duration; let second = Math.round(value * duration); this.curVideo.currentTime = second; this.sliderValue = value * 100; this.druationTime = this.formatSeconds(duration); this.currtime = this.formatSeconds(second); this.iosCurrentTime = second; if (duration) { if (type === 'start') { this.updateState = false; // 拖拽开始,停止自动更新 } if (type === 'end') { this.videoContext.seek(second); // 跳转到指定位置 this.curVideo.currentTime = second; this.updateState = true // 拖拽结束,恢复自动更新 } } } ``` ### 5. 全屏控制流程 #### 5.1 全屏切换 ```javascript fullScreen() { // 1. iOS特殊处理 const u = navigator.userAgent, app = navigator.appVersion; const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); if (isIOS) { document.querySelector("#xvideoPlayer").webkitEnterFullscreen() } // 2. 全屏状态切换 if (this.fullScreenFlag) { // 退出全屏 this.fullScreenFlag = false; this.videoFullHeight = 190 } else { // 进入全屏 this.fullScreenFlag = true; // 重新计算控制栏高度 uni.getSystemInfo({ success: function (info) { _this.videoFullHeight = info.windowHeight / 2 + 130; } }); } // 3. 触发父组件全屏事件 !isIOS && this.$listeners['fullscreenchange'](this.fullScreenFlag) } ``` #### 5.2 全屏状态变化处理 (onFullScreen) ```javascript onFullScreen(e) { this.fullScreenFlag = e.detail.fullScreen; let divId = 'videowatermark'; let full = e.detail.fullScreen; let divControlId = 'xplayer-control'; let box = document.getElementById('xvideoPlayer-box'); let control = document.getElementById(divControlId); setTimeout(() => { var div = document.getElementById("xvideoPlayer"); var div1 = div.firstChild; if (full) { // 全屏模式:添加水印和控制栏 if ($this.watermark) { // 创建水印元素 var div3 = document.createElement("div"); div3.id = divId; div3.setAttribute("class", "fullmark"); div3.innerHTML = ''; for (var i = 0; i < 4; i++) { div3.innerHTML += '