|
@@ -1,11 +1,11 @@
|
|
|
<template>
|
|
|
- <div
|
|
|
+ <div
|
|
|
class="video-player"
|
|
|
- :class="{ 'hide-controller': hideController }"
|
|
|
+ :class="{ 'hide-controller': hideController }"
|
|
|
:style="{
|
|
|
width: width * scale + 'px',
|
|
|
height: height * scale + 'px',
|
|
|
- transform: `scale(${1 / scale})`,
|
|
|
+ transform: `scale(${1 / scale})`
|
|
|
}"
|
|
|
@mousemove="autoHideController()"
|
|
|
@click="autoHideController()"
|
|
@@ -25,7 +25,10 @@
|
|
|
@timeupdate="handleTimeupdate()"
|
|
|
@ended="handleEnded()"
|
|
|
@progress="handleProgress()"
|
|
|
- @play="autoHideController(); paused = false"
|
|
|
+ @play="
|
|
|
+ autoHideController()
|
|
|
+ paused = false
|
|
|
+ "
|
|
|
@pause="autoHideController()"
|
|
|
@error="handleError()"
|
|
|
></video>
|
|
@@ -68,33 +71,35 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<span class="time">
|
|
|
- <span class="ptime">{{ptime}}</span> / <span class="dtime">{{dtime}}</span>
|
|
|
+ <span class="ptime">{{ ptime }}</span> / <span class="dtime">{{ dtime }}</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
<div class="icons icons-right">
|
|
|
<div class="speed">
|
|
|
<div class="icon speed-icon">
|
|
|
- <span class="icon-content" @click="speedMenuVisible = !speedMenuVisible">{{playbackRate === 1 ? '倍速' : (playbackRate + 'x')}}</span>
|
|
|
+ <span class="icon-content" @click="speedMenuVisible = !speedMenuVisible">{{ playbackRate === 1 ? "倍速" : playbackRate + "x" }}</span>
|
|
|
<div class="speed-menu" v-if="speedMenuVisible" @mouseleave="speedMenuVisible = false">
|
|
|
- <div
|
|
|
- class="speed-menu-item"
|
|
|
- :class="{ 'active': item.value === playbackRate }"
|
|
|
- v-for="item in speedOptions"
|
|
|
- :key="item.label"
|
|
|
+ <div
|
|
|
+ class="speed-menu-item"
|
|
|
+ :class="{ active: item.value === playbackRate }"
|
|
|
+ v-for="item in speedOptions"
|
|
|
+ :key="item.label"
|
|
|
@click="speed(item.value)"
|
|
|
- >{{item.label}}</div>
|
|
|
+ >
|
|
|
+ {{ item.label }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="loop" @click="toggleLoop()">
|
|
|
- <div class="icon loop-icon" :class="{ 'active': loop }">
|
|
|
- <span class="icon-content">循环{{loop ? '开' : '关'}}</span>
|
|
|
+ <div class="icon loop-icon" :class="{ active: loop }">
|
|
|
+ <span class="icon-content">循环{{ loop ? "开" : "关" }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div
|
|
|
+ <div
|
|
|
class="bar-wrap"
|
|
|
ref="playBarWrap"
|
|
|
@mousedown="handleMousedownPlayBar()"
|
|
@@ -103,7 +108,7 @@
|
|
|
@mouseenter="playBarTimeVisible = true"
|
|
|
@mouseleave="playBarTimeVisible = false"
|
|
|
>
|
|
|
- <div class="bar-time" :class="{ 'hidden': !playBarTimeVisible }" :style="{ left: playBarTimeLeft }">{{playBarTime}}</div>
|
|
|
+ <div class="bar-time" :class="{ hidden: !playBarTimeVisible }" :style="{ left: playBarTimeLeft }">{{ playBarTime }}</div>
|
|
|
<div class="bar">
|
|
|
<div class="loaded" :style="{ width: loadedBarWidth }"></div>
|
|
|
<div class="played" :style="{ width: playedBarWidth }">
|
|
@@ -116,30 +121,33 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { computed, ref } from 'vue'
|
|
|
-import useMSE from './useMSE'
|
|
|
-
|
|
|
-const props = withDefaults(defineProps<{
|
|
|
- width: number
|
|
|
- height: number
|
|
|
- src: string
|
|
|
- poster?: string
|
|
|
- autoplay?: boolean
|
|
|
- scale?: number
|
|
|
-}>(), {
|
|
|
- poster: '',
|
|
|
- autoplay: false,
|
|
|
- scale: 1,
|
|
|
-})
|
|
|
+import { computed, ref } from "vue"
|
|
|
+import useMSE from "./useMSE"
|
|
|
+
|
|
|
+const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ width: number
|
|
|
+ height: number
|
|
|
+ src: string
|
|
|
+ poster?: string
|
|
|
+ autoplay?: boolean
|
|
|
+ scale?: number
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ poster: "",
|
|
|
+ autoplay: false,
|
|
|
+ scale: 1
|
|
|
+ }
|
|
|
+)
|
|
|
|
|
|
const secondToTime = (second = 0) => {
|
|
|
- if (second === 0 || isNaN(second)) return '00:00'
|
|
|
+ if (second === 0 || isNaN(second)) return "00:00"
|
|
|
|
|
|
- const add0 = (num: number) => (num < 10 ? '0' + num : '' + num)
|
|
|
+ const add0 = (num: number) => (num < 10 ? "0" + num : "" + num)
|
|
|
const hour = Math.floor(second / 3600)
|
|
|
const min = Math.floor((second - hour * 3600) / 60)
|
|
|
const sec = Math.floor(second - hour * 3600 - min * 60)
|
|
|
- return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(':')
|
|
|
+ return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(":")
|
|
|
}
|
|
|
|
|
|
const getBoundingClientRectViewLeft = (element: HTMLElement) => {
|
|
@@ -160,23 +168,23 @@ const bezelTransition = ref(false)
|
|
|
const playbackRate = ref(1)
|
|
|
|
|
|
const playBarTimeVisible = ref(false)
|
|
|
-const playBarTime = ref('00:00')
|
|
|
-const playBarTimeLeft = ref('0')
|
|
|
+const playBarTime = ref("00:00")
|
|
|
+const playBarTimeLeft = ref("0")
|
|
|
|
|
|
const ptime = computed(() => secondToTime(currentTime.value))
|
|
|
const dtime = computed(() => secondToTime(duration.value))
|
|
|
-const playedBarWidth = computed(() => currentTime.value / duration.value * 100 + '%')
|
|
|
-const loadedBarWidth = computed(() => loaded.value / duration.value * 100 + '%')
|
|
|
-const volumeBarWidth = computed(() => volume.value * 100 + '%')
|
|
|
+const playedBarWidth = computed(() => (currentTime.value / duration.value) * 100 + "%")
|
|
|
+const loadedBarWidth = computed(() => (loaded.value / duration.value) * 100 + "%")
|
|
|
+const volumeBarWidth = computed(() => volume.value * 100 + "%")
|
|
|
|
|
|
const speedMenuVisible = ref(false)
|
|
|
const speedOptions = [
|
|
|
- { label: '2x', value: 2 },
|
|
|
- { label: '1.5x', value: 1.5 },
|
|
|
- { label: '1.25x', value: 1.25 },
|
|
|
- { label: '1x', value: 1 },
|
|
|
- { label: '0.75x', value: 0.75 },
|
|
|
- { label: '0.5x', value: 0.5 },
|
|
|
+ { label: "2x", value: 2 },
|
|
|
+ { label: "1.5x", value: 1.5 },
|
|
|
+ { label: "1.25x", value: 1.25 },
|
|
|
+ { label: "1x", value: 1 },
|
|
|
+ { label: "0.75x", value: 0.75 },
|
|
|
+ { label: "0.5x", value: 0.5 }
|
|
|
]
|
|
|
|
|
|
const seek = (time: number) => {
|
|
@@ -206,7 +214,7 @@ const pause = () => {
|
|
|
}
|
|
|
|
|
|
const toggle = () => {
|
|
|
- if (paused.value) play()
|
|
|
+ if (paused.value) play()
|
|
|
else pause()
|
|
|
}
|
|
|
|
|
@@ -247,11 +255,11 @@ const handleProgress = () => {
|
|
|
}
|
|
|
|
|
|
const loadError = ref(false)
|
|
|
-const handleError = () => loadError.value = true
|
|
|
+const handleError = () => (loadError.value = true)
|
|
|
|
|
|
const thumbMove = (e: MouseEvent | TouchEvent) => {
|
|
|
if (!videoRef.value || !playBarWrap.value) return
|
|
|
- const clientX = 'clientX' in e ? e.clientX : e.changedTouches[0].clientX
|
|
|
+ const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
|
|
|
let percentage = (clientX - getBoundingClientRectViewLeft(playBarWrap.value)) / playBarWrap.value.clientWidth
|
|
|
percentage = Math.max(percentage, 0)
|
|
|
percentage = Math.min(percentage, 1)
|
|
@@ -264,7 +272,7 @@ const thumbMove = (e: MouseEvent | TouchEvent) => {
|
|
|
const thumbUp = (e: MouseEvent | TouchEvent) => {
|
|
|
if (!videoRef.value || !playBarWrap.value) return
|
|
|
|
|
|
- const clientX = 'clientX' in e ? e.clientX : e.changedTouches[0].clientX
|
|
|
+ const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
|
|
|
let percentage = (clientX - getBoundingClientRectViewLeft(playBarWrap.value)) / playBarWrap.value.clientWidth
|
|
|
percentage = Math.max(percentage, 0)
|
|
|
percentage = Math.min(percentage, 1)
|
|
@@ -273,38 +281,38 @@ const thumbUp = (e: MouseEvent | TouchEvent) => {
|
|
|
videoRef.value.currentTime = time
|
|
|
currentTime.value = time
|
|
|
|
|
|
- document.removeEventListener('mousemove', thumbMove)
|
|
|
- document.removeEventListener('touchmove', thumbMove)
|
|
|
- document.removeEventListener('mouseup', thumbUp)
|
|
|
- document.removeEventListener('touchend', thumbUp)
|
|
|
+ document.removeEventListener("mousemove", thumbMove)
|
|
|
+ document.removeEventListener("touchmove", thumbMove)
|
|
|
+ document.removeEventListener("mouseup", thumbUp)
|
|
|
+ document.removeEventListener("touchend", thumbUp)
|
|
|
}
|
|
|
|
|
|
const handleMousedownPlayBar = () => {
|
|
|
- document.addEventListener('mousemove', thumbMove)
|
|
|
- document.addEventListener('touchmove', thumbMove)
|
|
|
- document.addEventListener('mouseup', thumbUp)
|
|
|
- document.addEventListener('touchend', thumbUp)
|
|
|
+ document.addEventListener("mousemove", thumbMove)
|
|
|
+ document.addEventListener("touchmove", thumbMove)
|
|
|
+ document.addEventListener("mouseup", thumbUp)
|
|
|
+ document.addEventListener("touchend", thumbUp)
|
|
|
}
|
|
|
|
|
|
const volumeMove = (e: MouseEvent | TouchEvent) => {
|
|
|
if (!volumeBarRef.value) return
|
|
|
- const clientX = 'clientX' in e ? e.clientX : e.changedTouches[0].clientX
|
|
|
+ const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
|
|
|
const percentage = (clientX - getBoundingClientRectViewLeft(volumeBarRef.value)) / 45
|
|
|
setVolume(percentage)
|
|
|
}
|
|
|
|
|
|
const volumeUp = () => {
|
|
|
- document.removeEventListener('mousemove', volumeMove)
|
|
|
- document.removeEventListener('touchmove', volumeMove)
|
|
|
- document.removeEventListener('mouseup', volumeUp)
|
|
|
- document.removeEventListener('touchend', volumeUp)
|
|
|
+ document.removeEventListener("mousemove", volumeMove)
|
|
|
+ document.removeEventListener("touchmove", volumeMove)
|
|
|
+ document.removeEventListener("mouseup", volumeUp)
|
|
|
+ document.removeEventListener("touchend", volumeUp)
|
|
|
}
|
|
|
|
|
|
const handleMousedownVolumeBar = () => {
|
|
|
- document.addEventListener('mousemove', volumeMove)
|
|
|
- document.addEventListener('touchmove', volumeMove)
|
|
|
- document.addEventListener('mouseup', volumeUp)
|
|
|
- document.addEventListener('touchend', volumeUp)
|
|
|
+ document.addEventListener("mousemove", volumeMove)
|
|
|
+ document.addEventListener("touchmove", volumeMove)
|
|
|
+ document.addEventListener("mouseup", volumeUp)
|
|
|
+ document.addEventListener("touchend", volumeUp)
|
|
|
}
|
|
|
|
|
|
const handleClickVolumeBar = (e: MouseEvent) => {
|
|
@@ -332,8 +340,7 @@ const toggleVolume = () => {
|
|
|
if (videoRef.value.muted) {
|
|
|
videoRef.value.muted = false
|
|
|
setVolume(0.5)
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
videoRef.value.muted = true
|
|
|
setVolume(0)
|
|
|
}
|
|
@@ -392,7 +399,8 @@ useMSE(props.src, videoRef)
|
|
|
}
|
|
|
|
|
|
.controller-mask {
|
|
|
- background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==) repeat-x bottom;
|
|
|
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==)
|
|
|
+ repeat-x bottom;
|
|
|
height: 98px;
|
|
|
width: 100%;
|
|
|
position: absolute;
|
|
@@ -517,7 +525,7 @@ useMSE(props.src, videoRef)
|
|
|
}
|
|
|
|
|
|
.icon-content {
|
|
|
- transition: all .2s ease-in-out;
|
|
|
+ transition: all 0.2s ease-in-out;
|
|
|
opacity: 0.8;
|
|
|
color: #fff;
|
|
|
}
|
|
@@ -690,4 +698,4 @@ useMSE(props.src, videoRef)
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|