# 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 += '
用户水印
'; } div1.appendChild(div3); } // 移动控制栏到视频内部 div1.appendChild(control); } else { // 退出全屏:移除水印,恢复控制栏位置 var hasControlDiv = div1.querySelector("#" + divControlId); if (hasControlDiv) { box.appendChild(hasControlDiv); } if ($this.watermark) { var markDiv = div1.querySelector("#" + divId); if (markDiv) { div1.removeChild(markDiv); } } } }, 200); this.$emit('fullscreenchange', e); } ``` ### 6. 倍速播放流程 #### 6.1 倍速控制显示 ```javascript showSpeedCtrl() { if (this.speedListShow) { this.speedListShow = false; } else { this.speedListShow = true; } } ``` #### 6.2 倍速切换 ```javascript changeSpeed(e) { // 1. 获取选择的倍速 let value = e; this.videoSpeed = Number(value); // 2. 设置播放器倍速 this.videoContext.playbackRate(this.videoSpeed); // 3. 保存倍速设置 studyUtil.setVideoSpeed(this.videoSpeed); // 4. 隐藏倍速选择框 this.speedListShow = false; } ``` ### 7. 监听器流程 #### 7.1 URL变化监听 ```javascript watch: { url(newVal, oldVal) { // 1. 停止当前播放 this.videoContext.stop(); this.playing = false; // 2. 重置播放状态 this.curVideo.currentTime = 0; this.iosCurrentTime = 0; this.sliderValue = 0; this.videoSpeed = 1.0; this.currtime = "00:00"; // 3. 重新加载视频 document.getElementsByTagName('video')[0].load() setTimeout(() => { document.getElementsByTagName('video')[0].play() }, 100) } } ``` ### 8. 触摸交互流程 #### 8.1 触摸结束处理 ```javascript onTouchend(e) { // 1. 清除控制栏隐藏定时器 if (this.contrlShowTimeout != null) { window.clearTimeout(this.contrlShowTimeout); } // 2. 控制栏显示/隐藏逻辑 if (!this.fullScreenFlag) { this.contrlShow = !this.contrlShow; } else { if (!this.contrlShow) { this.contrlShow = true; } } } ``` ## 组件事件 ### 对外事件 - `@loadeddata` - 视频数据加载完成 - `@loadedmetadata` - 视频元数据加载完成 - `@play` - 开始播放 - `@pause` - 播放暂停 - `@ended` - 播放结束 - `@timeupdate` - 播放进度更新 - `@fullscreenchange` - 全屏状态变化 - `@error` - 播放错误 ## 使用示例 ```vue ``` ## 关键特性 ### 1. 移动端优化 - 支持触摸手势控制 - 响应式布局适配 - iOS/Android兼容性处理 ### 2. 进度记录 - 本地存储播放进度 - 禁拖拽模式下自动记录 - 支持断点续播 ### 3. 安全特性 - 水印功能防录屏 - 可控制拖拽权限 - 播放行为监控 ### 4. 用户体验 - 自定义控制栏 - 倍速播放支持 - 全屏播放体验 - 自动隐藏控制栏 ## 注意事项 1. **兼容性**: 小米设备需要特殊处理,会显示兼容性提示 2. **权限控制**: `drag` 属性为false时禁止拖拽进度条 3. **进度保存**: 仅在禁拖拽模式下才会自动保存播放进度 4. **全屏处理**: iOS设备使用webkit全屏API,其他设备使用自定义全屏 5. **内存管理**: 组件销毁时需要清理定时器和事件监听器