diff --git a/components/video-player/README.md b/components/video-player/README.md new file mode 100644 index 0000000..69d96a8 --- /dev/null +++ b/components/video-player/README.md @@ -0,0 +1,488 @@ +# 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 += '