From 53c8483a3fb84a38b0dc36303d031f57cdb6e12c Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 3 May 2025 22:49:13 +0800 Subject: [PATCH] feat: auto play video --- src/components/VideoPlayer/index.tsx | 26 ++++----- src/lib/utils.ts | 10 ++++ src/services/video-manager.service.ts | 76 +++++++++++++-------------- 3 files changed, 62 insertions(+), 50 deletions(-) diff --git a/src/components/VideoPlayer/index.tsx b/src/components/VideoPlayer/index.tsx index 8b7c1c38..3b67933b 100644 --- a/src/components/VideoPlayer/index.tsx +++ b/src/components/VideoPlayer/index.tsx @@ -1,4 +1,4 @@ -import { cn } from '@/lib/utils' +import { cn, isInViewport } from '@/lib/utils' import videoManager from '@/services/video-manager.service' import { useEffect, useRef } from 'react' import NsfwOverlay from '../NsfwOverlay' @@ -25,11 +25,17 @@ export default function VideoPlayer({ const observer = new IntersectionObserver( ([entry]) => { - if (!entry.isIntersecting && !video.paused) { - videoManager.enterPiP(video) + if (entry.isIntersecting) { + setTimeout(() => { + if (isInViewport(container)) { + videoManager.autoPlay(video) + } + }, 200) + } else { + videoManager.pause(video) } }, - { threshold: 0.5 } + { threshold: 1 } ) observer.observe(container) @@ -39,13 +45,6 @@ export default function VideoPlayer({ } }, []) - const handlePlay = async () => { - const video = videoRef.current - if (!video) return - - await videoManager.playVideo(video) - } - return (
diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c7eeeb61..4faf2c55 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -11,3 +11,13 @@ export function isSafari() { const vendor = window.navigator.vendor return /Safari/.test(ua) && /Apple Computer/.test(vendor) && !/Chrome/.test(ua) } + +export function isInViewport(el: HTMLElement) { + const rect = el.getBoundingClientRect() + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + ) +} diff --git a/src/services/video-manager.service.ts b/src/services/video-manager.service.ts index 32c80b29..ab5f0bc0 100644 --- a/src/services/video-manager.service.ts +++ b/src/services/video-manager.service.ts @@ -6,59 +6,59 @@ class VideoManagerService { constructor() { if (!VideoManagerService.instance) { VideoManagerService.instance = this - document.addEventListener('leavepictureinpicture', (e) => { - ;(e.target as HTMLVideoElement).pause() - }) } return VideoManagerService.instance } - enterPiP(video: HTMLVideoElement) { - if (this.currentVideo && this.currentVideo !== video) { - this.exitPiP(this.currentVideo) + pause(video: HTMLVideoElement) { + if (isPipElement(video)) { + return } - - if ( - (video as any).webkitSupportsPresentationMode && - typeof (video as any).webkitSetPresentationMode === 'function' - ) { - ;(video as any).webkitSetPresentationMode('picture-in-picture') - setTimeout(() => { - if ((video as any).webkitPresentationMode !== 'picture-in-picture') { - video.pause() - } - }, 10) - } else { - video.requestPictureInPicture().catch(() => { - video.pause() - }) - } - } - - private exitPiP(video: HTMLVideoElement) { - video.pause() - if ( - (video as any).webkitSupportsPresentationMode && - typeof (video as any).webkitSetPresentationMode === 'function' - ) { - ;(video as any).webkitSetPresentationMode('inline') - } else { - document.exitPictureInPicture() - } - if (this.currentVideo === video) { this.currentVideo = null } + video.pause() } - async playVideo(video: HTMLVideoElement) { + autoPlay(video: HTMLVideoElement) { + if ( + document.pictureInPictureElement && + isVideoPlaying(document.pictureInPictureElement as HTMLVideoElement) + ) { + return + } + this.play(video) + } + + play(video: HTMLVideoElement) { + if (document.pictureInPictureElement && document.pictureInPictureElement !== video) { + ;(document.pictureInPictureElement as HTMLVideoElement).pause() + } if (this.currentVideo && this.currentVideo !== video) { - this.exitPiP(this.currentVideo) + this.currentVideo.pause() } this.currentVideo = video - video.play() + if (isVideoPlaying(video)) { + return + } + + this.currentVideo.play().catch((error) => { + console.error('Error playing video:', error) + this.currentVideo = null + }) } } const instance = new VideoManagerService() export default instance + +function isVideoPlaying(video: HTMLVideoElement) { + return video.currentTime > 0 && !video.paused && !video.ended && video.readyState >= 2 +} + +function isPipElement(video: HTMLVideoElement) { + if (document.pictureInPictureElement === video) { + return true + } + return (video as any).webkitPresentationMode === 'picture-in-picture' +}