mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-mobile.git
synced 2025-12-09 02:46:46 +08:00
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)
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 兼容性检测
function agentFullScreen() {
let bool = false
let agent = navigator.userAgent
if (agent.indexOf('XiaoMi') != -1) {
bool = true
}
return bool
}
2. 视频加载流程
2.1 元数据加载 (onMetaLoad)
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)
onLoad(e) {
// 隐藏控制栏
this.contrlShow = false;
// 触发父组件事件
this.$emit('loadeddata', e);
}
3. 播放控制流程
3.1 播放/暂停切换
videoOpreation() {
// 根据当前状态切换播放/暂停
this.playing ? this.videoContext.pause() : this.videoContext.play();
this.playing = !this.playing;
}
3.2 播放开始 (onPlay)
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)
onPause() {
this.playing = false;
this.$emit('pause')
}
3.4 播放结束 (onEnded)
onEnded() {
// 1. 停止播放
this.playing = false;
// 2. 退出全屏(如果在全屏状态)
if (this.fullScreenFlag) {
this.fullScreen()
}
// 3. 触发父组件结束事件
this.$emit('ended')
}
4. 进度控制流程
4.1 进度更新 (onTimeUpdate)
onTimeUpdate(e) {
// 1. 更新播放时间计数器
this.currtimeText = this.currtimeText + 1
// 2. 处理进度更新
this.videoUpdate(e);
// 3. 触发父组件时间更新事件
this.$emit('timeupdate', e)
}
4.2 详细进度处理 (videoUpdate)
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 进度条拖拽控制
// 点击进度条更新播放进度
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 全屏切换
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)
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 += '<div style="...">用户水印</div>';
}
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 倍速控制显示
showSpeedCtrl() {
if (this.speedListShow) {
this.speedListShow = false;
} else {
this.speedListShow = true;
}
}
6.2 倍速切换
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变化监听
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 触摸结束处理
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- 播放错误
使用示例
<template>
<video-player
:url="videoUrl"
:blobId="contentId"
:watermark="true"
:drag="true"
:inittime="0"
@play="onVideoPlay"
@pause="onVideoPause"
@ended="onVideoEnded"
@timeupdate="onTimeUpdate"
@fullscreenchange="onFullscreenChange"
/>
</template>
<script>
import VideoPlayer from '@/components/video-player/video-player.vue'
export default {
components: {
VideoPlayer
},
data() {
return {
videoUrl: 'https://example.com/video.mp4',
contentId: 'video_123'
}
},
methods: {
onVideoPlay() {
console.log('视频开始播放')
},
onVideoPause() {
console.log('视频暂停')
},
onVideoEnded() {
console.log('视频播放结束')
},
onTimeUpdate(e) {
console.log('播放进度更新:', e.detail)
},
onFullscreenChange(isFullscreen) {
console.log('全屏状态:', isFullscreen)
}
}
}
</script>
关键特性
1. 移动端优化
- 支持触摸手势控制
- 响应式布局适配
- iOS/Android兼容性处理
2. 进度记录
- 本地存储播放进度
- 禁拖拽模式下自动记录
- 支持断点续播
3. 安全特性
- 水印功能防录屏
- 可控制拖拽权限
- 播放行为监控
4. 用户体验
- 自定义控制栏
- 倍速播放支持
- 全屏播放体验
- 自动隐藏控制栏
注意事项
- 兼容性: 小米设备需要特殊处理,会显示兼容性提示
- 权限控制:
drag属性为false时禁止拖拽进度条 - 进度保存: 仅在禁拖拽模式下才会自动保存播放进度
- 全屏处理: iOS设备使用webkit全屏API,其他设备使用自定义全屏
- 内存管理: 组件销毁时需要清理定时器和事件监听器