import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from "vue" import "plyr/dist/plyr.css" import Plyr from "plyr" import styles from "./index.module.scss" import icons from "../../image/icons.json" const { iconVideoBg } = icons // eslint-disable-next-line @typescript-eslint/no-var-requires const iconLoopActive = require("../../image/iconLoopActive.png") // eslint-disable-next-line @typescript-eslint/no-var-requires const iconLoop = require("../../image/iconLoop.png") // eslint-disable-next-line @typescript-eslint/no-var-requires const iconPause = require("../../image/iconPause.png") // eslint-disable-next-line @typescript-eslint/no-var-requires const iconPlay = require("../../image/iconPlay.png") // eslint-disable-next-line @typescript-eslint/no-var-requires const iconSpeed = require("../../image/iconSpeed.png") import { ElSlider } from "element-plus" export default defineComponent({ name: "video-play", props: { item: { type: Object, default: () => { return {} } }, activeModel: { type: Boolean, default: true } }, emits: ["play", "pause", "ended", "close"], setup(props, { emit, expose }) { const { item } = toRefs(props) const data = reactive({ videoContianerRef: null as unknown as HTMLAudioElement, videoState: "pause" as "init" | "play" | "pause", animationState: "start" as "start" | "end", videoItem: null as unknown as Plyr, speedControl: false, speedStyle: { left: "1px" }, defaultSpeed: 1 // 默认速度 }) const controlID = "v" + Date.now() + Math.floor(Math.random() * 100) const playBtnId = "play" + Date.now() + Math.floor(Math.random() * 100) const loopBtnId = "loop" + Date.now() + Math.floor(Math.random() * 100) const speedBtnId = "speed" + Date.now() + Math.floor(Math.random() * 100) const togglePlay = (e: Event) => { e.stopPropagation() data.speedControl = false if (!data.videoContianerRef.paused) { data.videoItem?.pause() } else { data.videoContianerRef?.play() } } const toggleLoop = () => { data.speedControl = false const loopBtn = document.getElementById(loopBtnId) if (!loopBtn || !data.videoItem) return const isLoop = data.videoItem.loop if (isLoop) { loopBtn.classList.remove(styles.active) } else { loopBtn.classList.add(styles.active) } data.videoItem.loop = !data.videoItem.loop } const onDefault = () => { document.getElementById(controlID)?.addEventListener("click", (e: Event) => { e.stopPropagation() data.speedControl = false if (data.videoContianerRef.paused) return emit("close") }) document.getElementById(controlID)?.addEventListener("touchmove", () => { data.speedControl = false if (data.videoContianerRef.paused) return emit("close") }) document.getElementById(playBtnId)?.addEventListener("click", togglePlay) document.getElementById(loopBtnId)?.addEventListener("click", toggleLoop) document.getElementById(speedBtnId)?.addEventListener("click", e => { e.stopPropagation() data.speedControl = !data.speedControl }) setName() } const setName = () => { const nameEl = document.getElementById("videoItemName") if (nameEl) { nameEl.innerHTML = item.value.name || "" } } const changePlayBtn = (code: string) => { const playBtn = document.getElementById(playBtnId) if (!playBtn) return if (code == "play") { playBtn.classList.remove(styles.btnPause) playBtn.classList.add(styles.btnPlay) } else { playBtn.classList.remove(styles.btnPlay) playBtn.classList.add(styles.btnPause) } } const controls = `
00:00
00:00
% buffered 00:00
` onMounted(() => { data.videoItem = new Plyr(data.videoContianerRef, { autoplay: true, controls: controls, ratio: "16:9", // 强制所有视频的纵横比 hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。 clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停 fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏 }) nextTick(() => { onDefault() }) }) const toggleHideControl = (isShow: boolean) => { data.videoItem?.toggleControls(isShow) if (!isShow) { data.speedControl = isShow } } watch( () => props.activeModel, () => { toggleHideControl(props.activeModel) } ) watch( () => props.item, () => { setName() // 设置视屏播放器的默认速度 if (data.videoItem) data.videoItem.speed = data.defaultSpeed || 1 // 切换的时候隐藏 data.speedControl = false } ) let videoTimer = null as any const handlePlayVideo = () => { clearTimeout(videoTimer) nextTick(() => { data.videoContianerRef.play().catch(err => { console.log("🚀 ~ err:", err) videoTimer = setTimeout(() => { if (err?.message?.includes("play()")) { emit("play") } handlePlayVideo() }, 1000) }) }) } let videoErrorTimer = null as any let videoErrorCount = 0 const handleErrorVideo = () => { if (videoErrorCount > 5) { return } clearTimeout(videoErrorTimer) nextTick(() => { videoErrorTimer = setTimeout(() => { data.videoContianerRef.src = props.item?.content emit("play") data.videoContianerRef.load() // eslint-disable-next-line @typescript-eslint/no-unused-vars handleErrorVideo() }, 1000) }) videoErrorCount++ } const getVideoRef = () => { return data.videoContianerRef } const getVideoItem = () => { return data.videoItem } expose({ getVideoRef, getVideoItem }) return () => (
{ e.stopPropagation() }} > { if (data.defaultSpeed >= 1.5) { return } if (data.videoItem) { data.defaultSpeed = (data.defaultSpeed * 10 + 1) / 10 data.videoItem.speed = data.defaultSpeed } }} > { if (data.videoItem) { data.videoItem.speed = data.defaultSpeed } }} > {{ button: () => (
{data.defaultSpeed} x
) }}
{ if (data.defaultSpeed <= 0.5) { return } if (data.videoItem) { data.defaultSpeed = (data.defaultSpeed * 10 - 1) / 10 data.videoItem.speed = data.defaultSpeed } }} >
) } })