<template>
<div class="videoPlayer" ref="videoPlayer" @contextmenu.prevent="" @mouseleave="onMouseleave"
@mousemove="onMousemove">
<video class="fl_video" ref="flVideo" @mouseenter="onMouseenter" @canplay="getVidDur">
<source :src="Url" type='video/mp4'>
<source :src="Url" type='video/ogg'>
</video>
<div class="control" v-if="state.isController">
<div class="play" @click="onPlay">
<img v-if="state.isPlay" src="@/assets/img/new/video_suspend.png" alt="">
<img v-else src="@/assets/img/new/video_play.png" alt="">
</div>
<div class="progress">
<div class="slider-demo-block">
<el-slider v-model="state.progressValue" :show-tooltip="false" @change="handleChangeProgress" />
</div>
</div>
<div class="right">
<div class="time">
<span>{{state.currentTime}}</span><em>/</em><span>{{state.totalDuration}}</span>
</div>
<div class="volume">
<img v-if="state.isMute" src="@/assets/img/new/video_volume.png" alt="" @click="onVolume">
<img v-else src="@/assets/img/new/video_mute.png" alt="" @click="onVolume">
<div class="volume_slider">
<el-slider v-model="state.volume" vertical height="100px" @change="onChangeVolume" />
</div>
</div>
<div class="fullscreen" @click="onFullscreen">
<img v-if="state.isFullscreen" src="@/assets/img/new/video_quit_fullscreen.png" alt="" srcset="">
<img v-else src="@/assets/img/new/video_fullscreen.png" alt="" srcset="">
</div>
<slot></slot>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, reactive, ref, toRefs } from "vue";
import XEUtils from "xe-utils";
type TProps = {
Url: string
}
type TState = {
videoUrl: string
volume: number
isVolume: boolean
isMute: boolean
isPlay: boolean
isFullscreen: boolean
totalDuration: string
currentTime: string
timer: number
progressValue: number
percent: number
moveTimer: number
isController: boolean
mouseCoord: number
lastvalue: number
globalTimer: number
}
const state = reactive<TState>({
videoUrl: '',
volume: 50,
isVolume: false,
isMute: true,
isPlay: false,
isFullscreen: false,
totalDuration: '',
currentTime: '00:00',
timer: 0,
progressValue: 0,
percent: 0,
moveTimer: 0,
isController: true,
mouseCoord: 0,
lastvalue: 0,
globalTimer: 0
})
const videoPlayer = ref()
const flVideo = ref()
const emit = defineEmits(['played'])
onMounted(() => {
flVideo.value.volume = 0.5
loopTimer()
resizeListener()
})
const props = withDefaults(defineProps<TProps>(), {})
const Props = reactive(props)
const { Url } = toRefs(Props)
const getVidDur = () => {
getDuration()
}
const onMouseenter = (e: MouseEvent) => {
state.isController = true
clearTimeout(state.moveTimer)
}
const onMouseleave = () => {
state.moveTimer = setTimeout(() => {
state.isController = false
}, 2000)
}
const loopTimer = () => {
state.globalTimer = setInterval(() => {
if (state.lastvalue == state.mouseCoord) {
state.isController = false
}
state.lastvalue = state.mouseCoord
}, 3000)
}
const onMousemove = XEUtils.throttle((e: MouseEvent) => {
state.mouseCoord = e.pageX
state.isController = true
}, 300)
const onFullscreen = () => {
fullScreenToggle()
}
const resizeListener = () => {
window.addEventListener('resize', function () {
if (!isFullScreen()) {
if (!isFullScreen()) {
state.isFullscreen = false
}
}
})
}
const onVolume = () => {
if (state.isMute) {
flVideo.value.muted = true
state.isMute = false
} else {
flVideo.value.muted = false
state.isMute = true
}
}
const onPlay = () => {
if (!state.isPlay) {
flVideo.value.play()
state.isPlay = true
state.timer = setInterval(() => {
handleCurrentTime()
if (state.progressValue === 100) {
clearInterval(state.timer)
flVideo.value.pause()
state.isPlay = false
handlePlayedCallback()
}
}, 1000)
} else {
flVideo.value.pause()
state.isPlay = false
clearInterval(state.timer)
}
}
const onChangeVolume = (val: number) => {
state.volume = val
flVideo.value.volume = state.volume / 100
}
const getDuration = () => {
if (isNaN(flVideo.value.duration)) {
state.totalDuration = secondTurnTime(0)
} else {
state.totalDuration = secondTurnTime(flVideo.value.duration)
}
}
const handleCurrentTime = () => {
state.currentTime = secondTurnTime(flVideo.value.currentTime)
state.progressValue = (flVideo.value.currentTime / flVideo.value.duration) * 100
}
const handleChangeProgress = () => {
flVideo.value.currentTime = (state.progressValue / 100) * flVideo.value.duration
state.currentTime = secondTurnTime((state.progressValue / 100) * flVideo.value.duration)
}
const handlePlayedCallback = () => {
emit('played')
}
const secondTurnTime = (s: number) => {
let secondTime = Math.trunc(s)
let min = 0
let h = 0
let result = ''
if (secondTime >= 60) {
min = Math.trunc(secondTime / 60)
secondTime = Math.trunc(secondTime % 60)
if (min >= 60) {
h = Math.trunc(min / 60)
min = Math.trunc(min % 60)
}
}
if (h === 0) {
result = `${min.toString().padStart(2, '0')}:${secondTime.toString().padStart(2, '0')}`
} else {
result = `${h.toString().padStart(2, '0')}:${min.toString().padStart(2, '0')}:${secondTime.toString().padStart(2, '0')}`
}
return result
}
const fullScreen = () => {
const rfs = videoPlayer.value.requestFullScreen || videoPlayer.value.webkitRequestFullScreen || videoPlayer.value.mozRequestFullScreen || videoPlayer.value.msRequestFullscreen;
if (typeof rfs !== 'undefined' && rfs) {
rfs.call(videoPlayer.value);
}
}
const exitFullScreen = () => {
let doc: any = document
if (doc.exitFullscreen) {
doc.exitFullscreen();
} else if (doc.mozCancelFullScreen) {
doc.mozCancelFullScreen();
} else if (doc.webkitCancelFullScreen) {
doc.webkitCancelFullScreen();
} else if (doc.msExitFullscreen) {
doc.msExitFullscreen();
}
}
const isFullScreen = () => {
let doc: any = document
return doc.isFullScreen || doc.mozIsFullScreen || doc.webkitIsFullScreen
}
const fullScreenToggle = () => {
if (state.isFullscreen) {
exitFullScreen()
state.isFullscreen = false
} else {
fullScreen()
state.isFullscreen = true
}
}
onUnmounted(() => {
console.log('销毁');
clearTimeout(state.globalTimer)
})
</script>
<style lang="scss" scoped>
.videoPlayer {
width: 1209px;
height: 690px;
position: relative;
.fl_video {
background-color: #c5c4c4;
width: 100%;
height: 100%;
position: absolute;
z-index: 0;
}
.control {
width: 94%;
height: 56px;
position: absolute;
bottom: 30px;
left: 3%;
z-index: 10;
background: rgba(0, 0, 0, .4);
border-radius: 48px;
display: flex;
align-items: center;
padding: 10px 20px;
.play {
img {
height: 25px;
}
}
.progress {
width: 100%;
margin: 0 20px;
display: flex;
align-items: center;
.slider-demo-block {
width: 100%;
.el-slider {
width: 100%;
}
}
.total_progress {
position: relative;
width: 100%;
height: 6px;
border-radius: 3px;
background-color: #A5A5A5;
z-index: 100;
.buffer_progress {
position: absolute;
width: 50%;
height: 6px;
border-radius: 3px;
background-color: #CBCBCB;
z-index: 101;
}
.played_progress {
position: absolute;
width: 20%;
height: 6px;
border-radius: 3px;
background-color: #5182FF;
z-index: 102;
}
img {
position: absolute;
left: 20%;
top: -6px;
z-index: 103;
transform: translate(-50%);
}
}
}
.right {
display: flex;
flex-direction: row;
align-items: center;
margin-left: auto;
.time {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 16px;
em {
font-size: 16px;
color: #FFFFFF;
padding: 0 4px;
}
span {
font-size: 13px;
color: #FFFFFF;
}
}
.volume {
margin-left: 16px;
position: relative;
.volume_slider {
position: absolute;
bottom: 0;
left: -3px;
display: none;
margin-bottom: 36px;
:deep(.el-slider__button) {
border: solid 2px #A5A5A5
}
}
img {
width: 32px;
height: 32px;
}
&:hover {
.volume_slider {
display: block;
}
}
}
.fullscreen {
margin-left: 16px;
img {
width: 26px;
height: 26px;
}
}
}
}
}
video::-webkit-media-controls {
display: none;
}
</style>