mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-07 01:46:42 +08:00
917 lines
32 KiB
Vue
917 lines
32 KiB
Vue
<!-- 传入参数(props)-->
|
||
<!-- 必填项:src,-->
|
||
<!-- 可选项:width, height, speedList, cover, biBarrageXml-->
|
||
<template>
|
||
<div @contextmenu.prevent
|
||
ref="area"
|
||
:style="`--primaryColor: ${primaryColor}`"
|
||
:class="{'player-area': true, 'player-fullscreen': isFullscreen, 'cursor-lasting-static': isCursorStatic}"
|
||
>
|
||
<!-- 视频主体 -->
|
||
<video
|
||
ref="video"
|
||
class="player-video cursor-pointer"
|
||
@click="togglePlayStatus"
|
||
@keydown.f.prevent="toggleFullScreen"
|
||
@keydown.space.prevent="togglePlayStatus"
|
||
@keydown.left.prevent="backwardCurrentTime"
|
||
@keydown.right.prevent="forwardCurrentTime"
|
||
@keydown.up.prevent="increaseVolume"
|
||
@keydown.down.prevent="lowerVolume"
|
||
tabindex="0"
|
||
width="100%"
|
||
height="100%"
|
||
@waiting="onWaiting"
|
||
@playing="onPlaying"
|
||
@timeupdate="onAudioTimeUpdate"
|
||
>
|
||
<source :src="src" />
|
||
</video>
|
||
<!-- 封面图 -->
|
||
<div v-if="cover!=null && isShowCover" class="player-cover">
|
||
<img :src="cover" :width="width" :height="height" alt=""/>
|
||
</div>
|
||
<!-- 弹幕 -->
|
||
<playerBarrageScreen
|
||
v-if="biBarrageXml != null"
|
||
:videoDom="videoDom"
|
||
:barrageTimelineStart="barrageTimelineStart"
|
||
:isPlaying="isPlaying"
|
||
:biBarrageXml="biBarrageXml"
|
||
/>
|
||
<!-- 加载动画(应该当前视频没有缓存时显示加载动画) && isCrowd && isShowPlayer -->
|
||
<div v-show="isShowLoading" class="player-loading" @click="videoDom.focus({preventScroll: true})">
|
||
<img src="@/components/VideoPlayer/images/loading.svg" alt="loading"/>
|
||
</div>
|
||
<!-- 控制栏 -->
|
||
<div v-show="isCrowd" class="player-controls-container" @click="videoDom.focus({preventScroll: true})">
|
||
<div v-show="isShowVolumeHint" class="player-volumeHint">
|
||
<span class="player-volumeHint-text">当前音量:{{volumePercent}}%</span>
|
||
</div>
|
||
<div class="player-paused-state">
|
||
<svg
|
||
v-show="!isPlaying"
|
||
t="1596553913647"
|
||
class="player-controls-icon"
|
||
viewBox="0 0 1024 1024"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
p-id="13502"
|
||
width="32"
|
||
height="32"
|
||
>
|
||
<path
|
||
d="M897.143467 597.051733l-464.648534 311.5264c-46.976 31.488-110.592 18.944-142.08-28.023466A102.4 102.4 0 0 1 273.066667 823.5264V200.4736c0-56.5504 45.8496-102.4 102.4-102.4a102.4 102.4 0 0 1 57.028266 17.348267l464.64 311.5264c46.976 31.488 59.528533 95.104 28.032 142.08a102.4 102.4 0 0 1-28.023466 28.023466z"
|
||
p-id="13503"
|
||
fill="rgb(0,0,0,0.8)"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<div :class="{'player-controls': true, 'cursor-lasting-static': isCursorStatic}">
|
||
<div class="player-progress-bar">
|
||
<progressBar
|
||
:blobId="blobId"
|
||
:currentProgress="currentProgress"
|
||
v-on:updateProgress="updateProgressByClickBar"
|
||
v-on:getMouseDownStatus="getMouseDownStatusOfProgressBar"
|
||
:isDrag="isDrag"
|
||
width="100%"
|
||
height="4px"
|
||
></progressBar>
|
||
</div>
|
||
<div class="player-controls-bottom">
|
||
<div class="player-controls-bottom-left">
|
||
<div class="player-controls-btn cursor-pointer" @click="togglePlayStatus">
|
||
<svg
|
||
v-show="!isPlaying"
|
||
t="1596553913647"
|
||
class="player-controls-icon"
|
||
viewBox="0 0 1024 1024"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
p-id="13502"
|
||
width="32"
|
||
height="32"
|
||
>
|
||
<path
|
||
d="M897.143467 597.051733l-464.648534 311.5264c-46.976 31.488-110.592 18.944-142.08-28.023466A102.4 102.4 0 0 1 273.066667 823.5264V200.4736c0-56.5504 45.8496-102.4 102.4-102.4a102.4 102.4 0 0 1 57.028266 17.348267l464.64 311.5264c46.976 31.488 59.528533 95.104 28.032 142.08a102.4 102.4 0 0 1-28.023466 28.023466z"
|
||
p-id="13503"
|
||
fill="#ffffff"
|
||
/>
|
||
</svg>
|
||
<svg
|
||
v-show="isPlaying"
|
||
t="1596554115916"
|
||
class="player-controls-icon"
|
||
viewBox="0 0 1024 1024"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
p-id="17417"
|
||
width="32"
|
||
height="32"
|
||
>
|
||
<path
|
||
d="M268.97201558 114.31784297c73.21218086 0 132.56071902 59.34853814 132.56071901 132.560719v530.24287606c0 73.21218086-59.34853814 132.56071902-132.56071901 132.560719s-132.56071902-59.34853814-132.56071901-132.560719V246.87856197c0-73.21218086 59.34853814-132.56071902 132.56071901-132.560719z m486.05596884 0c73.21218086 0 132.56071902 59.34853814 132.56071901 132.560719v530.24287606c0 73.21218086-59.34853814 132.56071902-132.56071901 132.560719s-132.56071902-59.34853814-132.56071901-132.560719V246.87856197c0-73.21218086 59.34853814-132.56071902 132.56071901-132.560719z"
|
||
p-id="17418"
|
||
fill="#ffffff"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<div class="player-time">{{ currentTimeFormat }} / {{ fullTimeFormat }}</div>
|
||
</div>
|
||
<div class="player-controls-bottom-right">
|
||
<div class="player-controls-btn cursor-pointer btn-speed">
|
||
<span>{{currentSpeed === 1 ? '倍速' : `${currentSpeed}x`}}</span>
|
||
<div class="speed-control">
|
||
<ul class="speed-control-list">
|
||
<li
|
||
v-for="item in speedList"
|
||
:key="item"
|
||
@click="changeSpeed"
|
||
:data-value="item"
|
||
:class="{'current': currentSpeed === Number(item)}"
|
||
>{{ item }}x</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="player-controls-btn btn-volume">
|
||
<div class="cursor-pointer">
|
||
<svg
|
||
t="1596553801150"
|
||
class="player-controls-icon"
|
||
viewBox="0 0 1024 1024"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
p-id="12518"
|
||
width="32"
|
||
height="32"
|
||
>
|
||
<path
|
||
d="M308.971 657.987l150.28 165.279a16 16 0 0 0 11.838 5.236c8.837 0 16-7.163 16-16v-600.67a16 16 0 0 0-5.236-11.839c-6.538-5.944-16.657-5.463-22.602 1.075l-150.28 165.279A112 112 0 0 1 226.105 403H177c-17.673 0-32 14.327-32 32v154.333c0 17.674 14.327 32 32 32h49.105a112 112 0 0 1 82.866 36.654zM177 701.333c-61.856 0-112-50.144-112-112V435c0-61.856 50.144-112 112-112h49.105a32 32 0 0 0 23.676-10.472l150.28-165.28c35.668-39.227 96.383-42.113 135.61-6.445a96 96 0 0 1 31.418 71.028v600.671c0 53.02-42.98 96-96 96a96 96 0 0 1-71.029-31.417l-150.28-165.28a32 32 0 0 0-23.675-10.472H177z m456.058-348.336c-18.47-12.118-23.621-36.915-11.503-55.386 12.118-18.471 36.916-23.621 55.387-11.503C752.495 335.675 799 419.908 799 512c0 92.093-46.505 176.325-122.058 225.892-18.471 12.118-43.269 6.968-55.387-11.503-12.118-18.471-6.968-43.268 11.503-55.386C686.303 636.07 719 576.848 719 512c0-64.848-32.697-124.07-85.942-159.003z m92.93-137.323c-18.07-12.71-22.415-37.66-9.706-55.73s37.66-22.415 55.73-9.706C888.942 232.478 960 366.298 960 512s-71.058 279.522-187.988 361.762c-18.07 12.71-43.021 8.364-55.73-9.706-12.709-18.07-8.363-43.02 9.706-55.73C821.838 740.912 880 631.38 880 512c0-119.38-58.161-228.912-154.012-296.326z"
|
||
p-id="12519"
|
||
fill="#ffffff"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<div class="volume-control">
|
||
<div class="volume-control-wrap">
|
||
<div class="volume-text">{{ volumePercent }}</div>
|
||
<volumeBar
|
||
:currentVolume="currentVolume"
|
||
v-on:updateVolume="updateVolumeByClickBar"
|
||
width="4px"
|
||
></volumeBar>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="player-controls-btn cursor-pointer" @click="toggleFullScreen">
|
||
<svg
|
||
v-show="!isFullscreen"
|
||
class="player-controls-icon"
|
||
t="1596553111831"
|
||
viewBox="0 0 1024 1024"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
p-id="10800"
|
||
width="32"
|
||
height="32"
|
||
>
|
||
<path
|
||
d="M0 232.732444A232.732444 232.732444 0 0 1 232.732444 0h558.535112A232.732444 232.732444 0 0 1 1024 232.732444v558.535112A232.732444 232.732444 0 0 1 791.267556 1024H232.732444A232.732444 232.732444 0 0 1 0 791.267556V232.732444z m232.732444-139.662222a139.662222 139.662222 0 0 0-139.662222 139.662222v558.535112a139.662222 139.662222 0 0 0 139.662222 139.662222h558.535112a139.662222 139.662222 0 0 0 139.662222-139.662222V232.732444a139.662222 139.662222 0 0 0-139.662222-139.662222H232.732444z"
|
||
p-id="10801"
|
||
fill="#ffffff"
|
||
/>
|
||
<path
|
||
d="M549.575111 245.845333c0-25.799111 20.935111-46.734222 46.734222-46.734222h116.821334A140.202667 140.202667 0 0 1 853.333333 339.313778v116.821333a46.734222 46.734222 0 0 1-93.468444 0v-116.821333c0-25.827556-20.906667-46.734222-46.734222-46.734222h-116.821334a46.734222 46.734222 0 0 1-46.734222-46.734223zM245.845333 549.546667c25.799111 0 46.734222 20.935111 46.734223 46.734222v116.821333c0 25.827556 20.906667 46.734222 46.734222 46.734222h116.821333a46.734222 46.734222 0 0 1 0 93.468445h-116.821333A140.202667 140.202667 0 0 1 199.111111 713.130667v-116.821334c0-25.799111 20.935111-46.734222 46.734222-46.734222z"
|
||
p-id="10802"
|
||
fill="#ffffff"
|
||
/>
|
||
</svg>
|
||
<svg
|
||
v-show="isFullscreen"
|
||
t="1596958879235"
|
||
class="player-controls-icon"
|
||
viewBox="0 0 1024 1024"
|
||
version="1.1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
p-id="2734"
|
||
width="32"
|
||
height="32"
|
||
>
|
||
<path
|
||
d="M0.739556 233.130667a232.391111 232.391111 0 0 1 232.391111-232.391111h557.738666a232.391111 232.391111 0 0 1 232.391111 232.391111v557.738666a232.391111 232.391111 0 0 1-232.391111 232.391111H233.130667a232.391111 232.391111 0 0 1-232.391111-232.391111V233.130667z m232.391111-139.434667a139.434667 139.434667 0 0 0-139.434667 139.434667v557.738666a139.434667 139.434667 0 0 0 139.434667 139.434667h557.738666a139.434667 139.434667 0 0 0 139.434667-139.434667V233.130667a139.434667 139.434667 0 0 0-139.434667-139.434667H233.130667z"
|
||
p-id="2735"
|
||
fill="#ffffff"
|
||
/>
|
||
<path
|
||
d="M601.088 186.652444c25.685333 0 46.506667 20.792889 46.506667 46.478223v96.796444c0 25.685333 20.792889 46.478222 46.478222 46.478222h96.796444a46.478222 46.478222 0 1 1 0 92.984889h-96.796444a139.434667 139.434667 0 0 1-139.463111-139.463111V233.130667c0-25.685333 20.821333-46.478222 46.478222-46.478223z m-414.435556 414.435556c0-25.656889 20.792889-46.478222 46.478223-46.478222h96.796444a139.434667 139.434667 0 0 1 139.463111 139.463111v96.796444a46.478222 46.478222 0 0 1-92.984889 0v-96.796444c0-25.685333-20.792889-46.478222-46.478222-46.478222H233.130667a46.478222 46.478222 0 0 1-46.478223-46.506667z"
|
||
p-id="2736"
|
||
fill="#ffffff"
|
||
/>
|
||
</svg>
|
||
<div
|
||
class="player-controls-btn-hint btn-fullscreen-hint"
|
||
>{{isFullscreen ? '退出全屏' : '进入全屏'}}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import volumeBar from "@/components/VideoPlayer/volume-bar.vue";
|
||
import progressBar from "@/components/VideoPlayer/progress-bar.vue";
|
||
import playerBarrageScreen from "@/components/VideoPlayer/player-barrage-screen.vue";
|
||
|
||
export default {
|
||
name: "barrage-videoplayer",
|
||
components: { volumeBar, progressBar, playerBarrageScreen },
|
||
props: {
|
||
// 倍速列表
|
||
speedList: {
|
||
type: Array,
|
||
default: function () {
|
||
return ["2.0", "1.5", "1.25", "1.0", "0.75", "0.5"];
|
||
},
|
||
},
|
||
// 视频链接
|
||
src: {
|
||
type: String,
|
||
default: null,
|
||
},
|
||
// 视频链接对应的content Id
|
||
blobId: {
|
||
type: String,
|
||
default: null,
|
||
},
|
||
// 主题色
|
||
primaryColor: {
|
||
type: String,
|
||
default: "cornflowerblue",
|
||
},
|
||
// 封面图的链接
|
||
cover: {
|
||
type: String,
|
||
default: null,
|
||
},
|
||
// 弹幕的链接地址(XML文件格式,B站风格)
|
||
biBarrageXml: {
|
||
type: String,
|
||
default: null,
|
||
},
|
||
initTime:{//默认从第几秒播放
|
||
type: Number,
|
||
default: null,
|
||
},
|
||
notePlay:{//回到笔记时间点播放
|
||
type: Number,
|
||
default: null,
|
||
},
|
||
isDrag:{
|
||
type: Boolean,
|
||
default: true,
|
||
},
|
||
isCrowd:{
|
||
type: Boolean,
|
||
default: true,
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
isShowPlayer: true,//增加一个状态,判断加载动画
|
||
timeFastBack : 15, //单位秒,每次增减15秒
|
||
videoDom: null, //视频dom
|
||
isShowCover: true, // 是否显示封面
|
||
isFullscreen: false, // 是否处于全屏模式
|
||
isCursorStatic: false, // 鼠标是否长时间静止不动
|
||
isMousedownProgress: false, // 鼠标是否按下了进度条(并未松开)
|
||
isPlaying: false, // 是否正在播放
|
||
loadingError:false,//加载错误提示
|
||
isShowLoading: false, // 是否显示加载框
|
||
isShowVolumeHint: false, // 是否显示音量提示条(键盘触发)
|
||
timeoutVolumeHint: 0, // 音量提示条多久ms后隐藏
|
||
timeoutControlsHint: 0, // 控制面板多久ms后隐藏
|
||
currentSpeed: 1.0, // 当前倍速
|
||
volumePercent: 100, // 当前音量百分比(0-100),仅用于文字显示
|
||
currentVolume: 1, // 当前音量(0-1),同时作用于当前音量条的长度
|
||
currentProgress: 0, // 当前播放进度(0-1)。同时作用于当前进度条的长度
|
||
currentTimeFormat: "00:00:00", // 当前播放进度的文字
|
||
fullTimeFormat: "00:00:00", // 视频总长度的文字
|
||
barrageTimelineStart: 0, // 弹幕时间轴的起始时间点(手动调整进度条触发更新)
|
||
isInit:false, // 是否初始化过
|
||
};
|
||
},
|
||
created() {
|
||
|
||
},
|
||
mounted() {
|
||
this.videoDom = this.$refs.video;
|
||
this.videoDom.focus({preventScroll: true});
|
||
let speedValue=localStorage.getItem('boe_video_speed');
|
||
if(speedValue){
|
||
//console.log(speedValue,'speedValue');
|
||
this.currentSpeed=Number(speedValue);
|
||
this.videoDom.playbackRate=this.currentSpeed;
|
||
}
|
||
|
||
setInterval(() => {
|
||
//console.log('this.currentProgress::',this.currentProgress,this.isDrag,this.videoDom.currentTime , this.videoDom.duration)
|
||
// 视频播放时本地记录视频实时播放时长,视频设置了禁止拖动时执行
|
||
if(!this.isDrag){
|
||
var time = localStorage.getItem('videoProgressData')
|
||
var arr = time&&JSON.parse(time) || {}
|
||
if(arr[this.blobId] < this.currentProgress || !arr[this.blobId]){
|
||
arr[this.blobId] = parseFloat((this.currentProgress).toFixed(8))
|
||
if(arr[this.blobId]) localStorage.setItem('videoProgressData',JSON.stringify(arr))
|
||
}
|
||
}
|
||
|
||
// 定时更新进度条
|
||
if (this.isPlaying && !this.isMousedownProgress) {
|
||
this.currentProgress =
|
||
this.videoDom.currentTime / this.videoDom.duration;
|
||
}
|
||
// 定时更新进度的文字显示
|
||
this.updateProgressText();
|
||
// 音量提示面板的定时隐藏
|
||
if (this.timeoutVolumeHint == null) {
|
||
// 当定时为null,直接跳过
|
||
} else if (this.timeoutVolumeHint === 0) {
|
||
this.isShowVolumeHint = false;
|
||
this.isCursorStatic = false;
|
||
this.timeoutVolumeHint = null;
|
||
} else if (this.timeoutVolumeHint > 0) {
|
||
this.timeoutVolumeHint -= 1000;
|
||
} else {
|
||
this.timeoutVolumeHint = 0;
|
||
}
|
||
// 总控制面板的定时隐藏
|
||
if (
|
||
this.timeoutControlsHint == null ||
|
||
this.currentProgress === 0 ||
|
||
this.currentProgress === 1
|
||
) {
|
||
// 当定时为null、视频还未播放或播放完毕的时候,直接跳过
|
||
} else if (this.timeoutControlsHint === 0) {
|
||
this.isCursorStatic = true;
|
||
this.timeoutControlsHint = null;
|
||
} else if (this.timeoutControlsHint > 0) {
|
||
this.timeoutControlsHint -= 1000;
|
||
} else {
|
||
this.timeoutControlsHint = 0;
|
||
}
|
||
// 根据视频的readyState判断下一帧是否已加载,并控制loading的显示
|
||
this.isShowLoading = this.videoDom.readyState < 3;
|
||
//if()
|
||
//console.log(this.videoDom.readyState,'this.videoDom.readyState');
|
||
}, 1000);
|
||
// 视频dom监听器,用于控制鼠标的显示
|
||
this.videoDom.addEventListener("mousemove", () => {
|
||
this.isCursorStatic = false;
|
||
this.timeoutControlsHint = 2000;
|
||
});
|
||
this.videoDom.addEventListener('loadstart', function(){
|
||
console.log('\x1b[34m%s\x1b[0m','*****************视频加载中**************')
|
||
})
|
||
|
||
this.videoDom.addEventListener('loadeddata', function(){
|
||
console.log('\x1b[32m%s\x1b[0m','*****************加载完毕*************')
|
||
})
|
||
|
||
this.videoDom.addEventListener('error', function(){
|
||
console.log('\x1b[31m%s\x1b[0m','*****************加载失败**********')
|
||
})
|
||
// 监听全屏事件的变化,保存数据
|
||
window.addEventListener("F", () => {
|
||
this.isFullscreen = this.isFullScreen();
|
||
});
|
||
document.addEventListener('fullscreenchange',this.handleFullscreenChange);
|
||
document.addEventListener('webkitfullscreenchange',this.handleFullscreenChange );
|
||
// this.videoDom.addEventListener('keydown', function(e){
|
||
// //console.log('\x1b[31m%s\x1b[0m','*****************加载失败**********')
|
||
// console.log("video is exit");
|
||
// if(e.key === "Escape"){
|
||
// console.log("按键 退出全屏 执行")
|
||
// }
|
||
// })
|
||
|
||
// this.videoDom.addEventListener("keydown", (e) => {
|
||
// console.log("是否监听到");
|
||
// if(e.keyCode == 27){
|
||
// console.log("是否监听到 esc");
|
||
// this.toggleFullScreen();
|
||
// }
|
||
// });
|
||
},
|
||
methods: {
|
||
//当视频由于需要缓冲下一帧而停止,解决一直计时的问题
|
||
onWaiting(){
|
||
console.log('触发了onWairing');
|
||
this.isShowLoading = true
|
||
this.$emit('onPlayerPause', {})
|
||
},
|
||
//当音频/视频在已因缓冲而暂停或停止后已就绪时
|
||
onPlaying(){
|
||
console.log('触发缓存结束onPlaying');
|
||
this.isShowLoading = false
|
||
if(this.videoDom.paused){
|
||
this.$emit('onPlayerPause', {})
|
||
}else{
|
||
this.$emit('onPlayerPlay', {});
|
||
}
|
||
},
|
||
/* 切换播放状态
|
||
*/
|
||
togglePlayStatus() {
|
||
if (this.videoDom.paused) {
|
||
if(!this.isInit && this.initTime != null && this.initTime > 0){
|
||
this.videoDom.currentTime = this.initTime;
|
||
this.barrageTimelineStart = this.initTime;
|
||
this.updateProgressBySetTime(this.initTime);
|
||
this.updateProgressText();
|
||
}
|
||
this.videoDom.play();
|
||
this.isPlaying = true;
|
||
this.isInit = true;
|
||
this.$emit('onPlayerPlay', {});//播放(播放时会调用)
|
||
console.log('播放');
|
||
} else {
|
||
this.videoDom.pause();
|
||
this.isPlaying = false;
|
||
this.$emit('onPlayerPause', {})//暂停(暂停时调用)
|
||
}
|
||
this.isShowCover = false;
|
||
},
|
||
/* 更新视频进度的文字显示
|
||
*/
|
||
updateProgressText() {
|
||
this.currentTimeFormat = this.secondTimeFormat(
|
||
this.videoDom.currentTime
|
||
);
|
||
this.fullTimeFormat = this.secondTimeFormat(this.videoDom.duration);
|
||
},
|
||
/* 时间格式化,秒格式化成xx:xx:xx
|
||
*/
|
||
secondTimeFormat(second) {
|
||
let result = parseInt(second);
|
||
let h =
|
||
Math.floor(result / 3600) < 10
|
||
? "0" + Math.floor(result / 3600)
|
||
: Math.floor(result / 3600);
|
||
let m =
|
||
Math.floor((result / 60) % 60) < 10
|
||
? "0" + Math.floor((result / 60) % 60)
|
||
: Math.floor((result / 60) % 60);
|
||
let s =
|
||
Math.floor(result % 60) < 10
|
||
? "0" + Math.floor(result % 60)
|
||
: Math.floor(result % 60);
|
||
result = `${h}:${m}:${s}`;
|
||
return result;
|
||
},
|
||
/* 前进视频播放进度
|
||
*/
|
||
forwardCurrentTime() {
|
||
let newCurrentTime = this.videoDom.currentTime + this.timeFastBack;
|
||
this.videoDom.currentTime = newCurrentTime;
|
||
this.barrageTimelineStart = newCurrentTime;
|
||
this.updateProgressBySetTime(newCurrentTime);
|
||
},
|
||
/* 后退视频播放进度
|
||
*/
|
||
backwardCurrentTime() {
|
||
let newCurrentTime = this.videoDom.currentTime - this.timeFastBack;
|
||
this.videoDom.currentTime = newCurrentTime;
|
||
this.barrageTimelineStart = newCurrentTime;
|
||
this.updateProgressBySetTime(newCurrentTime);
|
||
},
|
||
/* 获取鼠标是否按下了进度条
|
||
*/
|
||
getMouseDownStatusOfProgressBar(value) {
|
||
this.isMousedownProgress = value;
|
||
},
|
||
/* 点击进度条更新视频播放进度
|
||
*/
|
||
updateProgressByClickBar(value) {
|
||
let duration = this.videoDom.duration;
|
||
this.currentProgress = value;
|
||
let new_current_time = Math.round(value * duration);
|
||
this.barrageTimelineStart = new_current_time;
|
||
this.videoDom.currentTime = new_current_time;
|
||
},
|
||
/* 通过新的播放时间更新视频播放进度
|
||
*/
|
||
updateProgressBySetTime(newCurrentTime) {
|
||
this.currentProgress = newCurrentTime / this.videoDom.duration;
|
||
},
|
||
/* 点击进度条更新视频播放进度2
|
||
*/
|
||
updateProgressByClickBar2(newCurrentTime,progressVideo) {
|
||
this.currentProgress = progressVideo;
|
||
this.barrageTimelineStart = newCurrentTime;
|
||
this.videoDom.currentTime = newCurrentTime;
|
||
this.updateProgressText();
|
||
},
|
||
/* 提高视频音量
|
||
*/
|
||
increaseVolume() {
|
||
this.isShowVolumeHint = true;
|
||
this.timeoutVolumeHint = 2000;
|
||
let nowVolume = this.videoDom.volume;
|
||
if (nowVolume >= 0.9) {
|
||
this.videoDom.volume = 1;
|
||
this.currentVolume = 1;
|
||
} else {
|
||
let newVolume = this.videoDom.volume + 0.1;
|
||
this.videoDom.volume = newVolume;
|
||
this.currentVolume = newVolume;
|
||
}
|
||
},
|
||
/* 降低视频音量
|
||
*/
|
||
lowerVolume() {
|
||
this.isShowVolumeHint = true;
|
||
this.timeoutVolumeHint = 2000;
|
||
let nowVolume = this.videoDom.volume;
|
||
if (nowVolume <= 0.1) {
|
||
this.videoDom.volume = 0;
|
||
this.currentVolume = 0;
|
||
} else {
|
||
let newVolume = this.videoDom.volume - 0.1;
|
||
this.videoDom.volume = newVolume;
|
||
this.currentVolume = newVolume;
|
||
}
|
||
},
|
||
/* 视频倍速播放
|
||
*/
|
||
changeSpeed(e) {
|
||
// 获取选择的倍速
|
||
let value = e.currentTarget.dataset.value;
|
||
localStorage.setItem('boe_video_speed',value);
|
||
// 应用视频倍速
|
||
this.videoDom.playbackRate = value;
|
||
// 标记变更后的倍速,用于显示文字
|
||
this.currentSpeed = Number(value);
|
||
},
|
||
/* 点击音量条后更新音量(value范围:0-1)
|
||
*/
|
||
updateVolumeByClickBar(value) {
|
||
this.videoDom.volume = value;
|
||
this.currentVolume = value;
|
||
},
|
||
/* 切换“全屏”和“非全屏”模式
|
||
*/
|
||
toggleFullScreen() {
|
||
console.log("toggleFullScreen 执行 ",this.isFullScreen())
|
||
let element = this.$refs.area;
|
||
if (!this.isFullScreen()) {
|
||
if (element.requestFullscreen) {
|
||
element.requestFullscreen();
|
||
} else if(element.webkitRequestFullscreen) {
|
||
element.webkitRequestFullscreen();
|
||
} else if (element.mozRequestFullScreen) {
|
||
element.mozRequestFullScreen();
|
||
} else if (element.msRequestFullscreen) {
|
||
element.msRequestFullscreen();
|
||
} else if (element.oRequestFullscreen) {
|
||
element.oRequestFullscreen();
|
||
}
|
||
this.isFullscreen = true;
|
||
this.$emit('onFullscreen',true);//全屏
|
||
} else {
|
||
if (document.exitFullscreen) {
|
||
document.exitFullscreen();
|
||
} else if (document.msExitFullscreen) {
|
||
document.msExitFullscreen();
|
||
} else if (document.mozCancelFullScreen) {
|
||
document.mozCancelFullScreen();
|
||
} else if (document.oRequestFullscreen) {
|
||
document.oCancelFullScreen();
|
||
} else if (document.webkitExitFullscreen) {
|
||
document.webkitExitFullscreen();
|
||
}
|
||
this.isFullscreen = false;
|
||
this.$emit('onFullscreen',false);//全屏
|
||
}
|
||
},
|
||
/* 判断是否进入了全屏模式
|
||
*/
|
||
isFullScreen: function () {
|
||
return !!(
|
||
document.fullscreen ||
|
||
document.mozFullScreen ||
|
||
document.webkitIsFullScreen ||
|
||
document.webkitFullScreen ||
|
||
document.msFullScreen
|
||
);
|
||
},
|
||
//开始播放
|
||
startPlay:function(newTime){
|
||
if(newTime){
|
||
this.videoDom.currentTime = newTime;
|
||
this.barrageTimelineStart = newTime;
|
||
this.updateProgressBySetTime(newTime);
|
||
}
|
||
this.isPlaying = true;
|
||
this.videoDom.play();
|
||
},
|
||
onAudioTimeUpdate() {
|
||
const currentTime = this.$refs.video.currentTime;
|
||
this.$emit('onTimeUpdate', currentTime);
|
||
},
|
||
/**
|
||
* 监听全屏变化
|
||
*/
|
||
handleFullscreenChange() {
|
||
console.log("handleFullscreenChange gx this.isFullScreen()",this.isFullScreen())
|
||
console.log("handleFullscreenChange gx this.isFullscreen",this.isFullscreen)
|
||
|
||
if(this.isFullscreen){
|
||
this.isFullscreen = !!document.fullscreenElement;
|
||
if (!this.isFullscreen) {
|
||
// 退出全屏后的逻辑(如暂停视频、更新 UI 等)
|
||
console.log('ESC 已退出全屏 handleFullscreenChange',this.isFullscreen);
|
||
this.$emit('onFullscreen',false);//全屏
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
},
|
||
watch: {
|
||
currentVolume: function () {
|
||
// 根据当前音量大小设置显示的音量文字
|
||
this.volumePercent = Math.round(this.currentVolume * 100);
|
||
},
|
||
currentProgress: function () {
|
||
// 进度条到终点时修改播放状态
|
||
this.isShowPlayer = false
|
||
this.$emit('onPlayerPlaying', this.videoDom.currentTime,this.videoDom.duration)
|
||
this.$emit('progress',this.currentProgress)
|
||
if (this.currentProgress === 1) {
|
||
this.isPlaying = false;
|
||
this.$emit('onPlayerEnded', {})
|
||
}
|
||
},
|
||
// notePlay: function(val) {
|
||
// if(val) {
|
||
// this.updateProgressBySetTime(val);
|
||
// this.isPlaying = true;
|
||
// this.videoDom.play();
|
||
// }
|
||
// },
|
||
src: function () {
|
||
// 当视频地址变更时,重载视频
|
||
this.videoDom.load();
|
||
this.isPlaying = false
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.player-area {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
display: inline-block;
|
||
-webkit-user-select: none;
|
||
-moz-user-select: none;
|
||
-ms-user-select: none;
|
||
user-select: none;
|
||
}
|
||
.cursor-pointer {
|
||
cursor: pointer;
|
||
}
|
||
.cursor-lasting-static .cursor-pointer {
|
||
cursor: none;
|
||
}
|
||
.cursor-lasting-static.player-controls {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
}
|
||
.player-video {
|
||
outline: none;
|
||
vertical-align: middle;
|
||
object-fit: contain;
|
||
}
|
||
.player-cover {
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
}
|
||
.player-loading {
|
||
position: absolute;
|
||
top: 50%;
|
||
bottom: 50%;
|
||
left: 50%;
|
||
right: 50%;
|
||
width: 80px;
|
||
height: 80px;
|
||
transform: translate(-50%, -50%);
|
||
}
|
||
.player-loading img {
|
||
fill: #fff;
|
||
width: inherit;
|
||
height: inherit;
|
||
}
|
||
.player-paused-state {
|
||
position: absolute;
|
||
pointer-events: none;
|
||
top: 0;
|
||
left: 2%;
|
||
transform: translate(0, -100%);
|
||
border-radius: 10px;
|
||
box-shadow: 0 0 20px #bbbbbb;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
}
|
||
.player-paused-state svg {
|
||
/* margin: 0.4rem 1rem; */
|
||
margin: 1rem 2em 0.8em 2em;
|
||
}
|
||
.player-controls-container {
|
||
position: absolute;
|
||
bottom: 0;
|
||
width: 100%;
|
||
}
|
||
.player-volumeHint {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 12px;
|
||
pointer-events: none;
|
||
transform: translate(0, -100%);
|
||
border-radius: 4px;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
}
|
||
.player-volumeHint-text {
|
||
position: relative;
|
||
display: inline-block;
|
||
padding: 0.4rem 0.6rem;
|
||
color: #fff;
|
||
font-size: 1rem;
|
||
}
|
||
.player-controls {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
position: relative;
|
||
display: block;
|
||
transition: visibility 0.3s, opacity 0.3s;
|
||
background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
|
||
}
|
||
.player-controls-container:hover .player-controls {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
.player-controls-container:hover .cursor-pointer {
|
||
cursor: pointer;
|
||
}
|
||
.player-controls-bottom {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
}
|
||
.player-controls-bottom-left {
|
||
padding: 0.6rem 0.4rem;
|
||
display: inline-flex;
|
||
align-items:center;
|
||
justify-content:center;
|
||
}
|
||
.player-controls-bottom-right {
|
||
display: inline-flex;
|
||
padding: 0.6rem 0.4rem;
|
||
align-items:center;
|
||
justify-content:center;
|
||
}
|
||
.player-controls-btn {
|
||
position: relative;
|
||
display: inline-block;
|
||
color: #e5e5e5;
|
||
padding: 0 0.4rem;
|
||
transition: color 0.3s;
|
||
height: 22px;
|
||
}
|
||
.player-controls-btn .player-controls-icon {
|
||
height: 22px;
|
||
transition: height 0.3s;
|
||
}
|
||
.player-controls-btn svg path {
|
||
fill: #e5e5e5;
|
||
transition: fill 0.2s;
|
||
}
|
||
.player-controls-btn:hover svg > path {
|
||
fill: #ffffff;
|
||
}
|
||
.player-controls-btn:hover {
|
||
color: #fff;
|
||
}
|
||
.player-controls-btn:hover .player-controls-btn-hint {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
.player-controls-btn-hint {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 50%;
|
||
white-space: nowrap;
|
||
padding: 0.4rem 0.6rem;
|
||
border-radius: 4px;
|
||
background: rgba(21, 21, 21, 0.8);
|
||
transition: opacity 0.3s, visibility 0s;
|
||
transform: translate(-50%, -200%);
|
||
}
|
||
.btn-fullscreen-hint {
|
||
font-size: 12px;
|
||
margin-left: -16px;
|
||
}
|
||
.player-time {
|
||
display: inline-block;
|
||
color: #e5e5e5;
|
||
height: 16px;
|
||
}
|
||
.btn-speed:hover .speed-control {
|
||
visibility: visible;
|
||
}
|
||
.speed-control {
|
||
visibility: hidden;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 50%;
|
||
padding-bottom: 24px;
|
||
transition: visibility 0.3s;
|
||
transform: translate(-50%, -100%);
|
||
}
|
||
.speed-control .speed-control-list {
|
||
list-style: none;
|
||
color: #e5e5e5;
|
||
width: 50px;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
padding: 0;
|
||
margin: 0;
|
||
overflow: hidden;
|
||
border-radius: 4px;
|
||
background: rgba(21, 21, 21, 0.8);
|
||
}
|
||
.speed-control .speed-control-list li {
|
||
position: relative;
|
||
display: block;
|
||
height: 25px;
|
||
line-height: 25px;
|
||
}
|
||
.speed-control .speed-control-list li:hover {
|
||
color: #fff;
|
||
background: rgba(99, 99, 99, 0.8);
|
||
}
|
||
.speed-control .speed-control-list li.current {
|
||
color: var(--primaryColor);
|
||
}
|
||
.btn-volume:hover .volume-control {
|
||
visibility: visible;
|
||
}
|
||
.volume-control {
|
||
visibility: hidden;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 50%;
|
||
padding: 24px 10px;
|
||
transition: visibility 0.3s;
|
||
transform: translate(-50%, -100%);
|
||
}
|
||
.volume-control-wrap {
|
||
display: flex;
|
||
text-align: center;
|
||
flex-direction: column;
|
||
margin: auto;
|
||
width: 35px;
|
||
height: 120px;
|
||
padding: 0.6rem 0 1rem;
|
||
background: rgba(21, 21, 21, 0.8);
|
||
border-radius: 4px;
|
||
}
|
||
.volume-text {
|
||
font-size: 12px;
|
||
color: #fff;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
@media (device-width: 100vw) {
|
||
.player-controls-btn .player-controls-icon {
|
||
/* height: 26px; */
|
||
}
|
||
}
|
||
@media (min-width: 1000px) {
|
||
.player-loading {
|
||
width: 100px;
|
||
height: 100px;
|
||
}
|
||
}
|
||
</style>
|