diff --git a/components/player/MobileVideoPlayer.tsx b/components/player/MobileVideoPlayer.tsx index c982755..e3730da 100644 --- a/components/player/MobileVideoPlayer.tsx +++ b/components/player/MobileVideoPlayer.tsx @@ -2,7 +2,7 @@ import { useRef, useState, useEffect } from 'react'; import { Icons } from '@/components/ui/Icon'; -import { useDoubleTap, useScreenOrientation } from '@/lib/hooks/useMobilePlayer'; +import { useDoubleTap, useScreenOrientation, useIsIOS } from '@/lib/hooks/useMobilePlayer'; interface MobileVideoPlayerProps { src: string; @@ -43,6 +43,7 @@ export function MobileVideoPlayer({ const [toastMessage, setToastMessage] = useState(null); const [showToast, setShowToast] = useState(false); const [viewportWidth, setViewportWidth] = useState(0); + const isIOS = useIsIOS(); const controlsTimeoutRef = useRef(null); const skipTimeoutRef = useRef(null); @@ -394,6 +395,12 @@ export function MobileVideoPlayer({ if (!containerRef.current) return; if (!isFullscreen) { + // iOS specific handling + if (isIOS && videoRef.current && (videoRef.current as any).webkitEnterFullscreen) { + (videoRef.current as any).webkitEnterFullscreen(); + return; + } + // Use container fullscreen to maintain custom controls // Try standard fullscreen API first if (containerRef.current.requestFullscreen) { @@ -599,9 +606,6 @@ export function MobileVideoPlayer({ e.stopPropagation(); togglePlay(); }} - onTouchEnd={(e) => { - e.stopPropagation(); - }} className={`btn-icon ${buttonPadding} flex-shrink-0 touch-manipulation relative z-[60]`} aria-label={isPlaying ? 'Pause' : 'Play'} style={{ WebkitTapHighlightColor: 'transparent' }} @@ -624,11 +628,6 @@ export function MobileVideoPlayer({ e.stopPropagation(); setShowMoreMenu(!showMoreMenu); }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.preventDefault(); - setShowMoreMenu(!showMoreMenu); - }} className={`btn-icon ${buttonPadding} flex-shrink-0 touch-manipulation`} aria-label="更多" style={{ WebkitTapHighlightColor: 'transparent' }} @@ -651,12 +650,6 @@ export function MobileVideoPlayer({ setShowMoreMenu(false); handleCopyLink(); }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.preventDefault(); - setShowMoreMenu(false); - handleCopyLink(); - }} className="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/20 flex items-center gap-3 transition-all touch-manipulation" style={{ WebkitTapHighlightColor: 'transparent' }} > @@ -673,12 +666,6 @@ export function MobileVideoPlayer({ setShowMoreMenu(false); setShowVolumeMenu(true); }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.preventDefault(); - setShowMoreMenu(false); - setShowVolumeMenu(true); - }} className="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/20 flex items-center gap-3 transition-all touch-manipulation" style={{ WebkitTapHighlightColor: 'transparent' }} > @@ -693,12 +680,6 @@ export function MobileVideoPlayer({ setShowMoreMenu(false); setShowSpeedMenu(true); }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.preventDefault(); - setShowMoreMenu(false); - setShowSpeedMenu(true); - }} className="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/20 flex items-center gap-3 transition-all touch-manipulation" style={{ WebkitTapHighlightColor: 'transparent' }} > @@ -717,12 +698,6 @@ export function MobileVideoPlayer({ setShowMoreMenu(false); togglePictureInPicture(); }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.preventDefault(); - setShowMoreMenu(false); - togglePictureInPicture(); - }} className="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/20 flex items-center gap-3 transition-all touch-manipulation" style={{ WebkitTapHighlightColor: 'transparent' }} > @@ -741,11 +716,6 @@ export function MobileVideoPlayer({ e.stopPropagation(); toggleFullscreen(); }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.preventDefault(); - toggleFullscreen(); - }} className={`btn-icon ${buttonPadding} flex-shrink-0 touch-manipulation relative z-[60]`} aria-label={isFullscreen ? '退出全屏' : '全屏'} style={{ WebkitTapHighlightColor: 'transparent' }} diff --git a/lib/hooks/useMobilePlayer.ts b/lib/hooks/useMobilePlayer.ts index ceeeecf..1119617 100644 --- a/lib/hooks/useMobilePlayer.ts +++ b/lib/hooks/useMobilePlayer.ts @@ -33,7 +33,7 @@ export function useDoubleTap({ const currentTime = Date.now(); const videoElement = e.currentTarget; const touch = e.touches[0] || e.changedTouches[0]; - + if (!touch || !videoElement) return; // Calculate touch position relative to video element @@ -65,19 +65,19 @@ export function useDoubleTap({ // Double tap detected (within 300ms on the same side) if (timeDiff < 300 && sameSide) { e.preventDefault(); - + if (side === 'left') { onDoubleTapLeft(); } else { onDoubleTapRight(); } - + // Reset to prevent triple-tap lastTapRef.current = { time: 0, side: null }; } else { // Possible single tap - wait to see if there's a double tap lastTapRef.current = { time: currentTime, side }; - + singleTapTimeoutRef.current = setTimeout(() => { // After 300ms, no double tap detected, execute single tap action onSingleTap(); @@ -100,7 +100,7 @@ export function useScreenOrientation(isFullscreen: boolean) { const handleOrientation = async () => { try { const screen = window.screen as any; - + if (isFullscreen) { // Fullscreen: Lock to landscape if (screen.orientation?.lock) { @@ -157,3 +157,21 @@ export function useIsMobile() { return isMobile; } + +/** + * Hook to detect if the device is iOS + */ +export function useIsIOS() { + const [isIOS, setIsIOS] = useState(false); + + useEffect(() => { + const checkIOS = () => { + const ios = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream; + setIsIOS(ios); + }; + + checkIOS(); + }, []); + + return isIOS; +}