diff --git a/src/components/mediaViewer/VideoPlayer.tsx b/src/components/mediaViewer/VideoPlayer.tsx index c9c259ba8..495c2704e 100644 --- a/src/components/mediaViewer/VideoPlayer.tsx +++ b/src/components/mediaViewer/VideoPlayer.tsx @@ -22,7 +22,7 @@ import usePictureInPicture from '../../hooks/usePictureInPicture'; import useShowTransitionDeprecated from '../../hooks/useShowTransitionDeprecated'; import useVideoCleanup from '../../hooks/useVideoCleanup'; import useFullscreen from '../../hooks/window/useFullscreen'; -import useControlsSignal from './hooks/useControlsSignal'; +import useControlsSignal, { registerPlayerElement } from './hooks/useControlsSignal'; import useVideoWaitingSignal from './hooks/useVideoWaitingSignal'; import Button from '../ui/Button'; @@ -113,7 +113,7 @@ const VideoPlayer: FC = ({ const [, toggleControls, lockControls] = useControlsSignal(); const [getIsSeeking, setIsSeeking] = useSignal(false); - const lastMousePositionRef = useRef({ x: 0, y: 0 }); + const lastMousePositionRef = useRef<{ x: number; y: number }>(); useEffect(() => { const updateMousePosition = (e: MouseEvent | TouchEvent) => { @@ -129,6 +129,11 @@ const VideoPlayer: FC = ({ }; }, []); + useEffect(() => { + registerPlayerElement(videoRef.current, () => lastMousePositionRef.current); + return () => registerPlayerElement(undefined); + }, []); + const checkMousePositionAndToggleControls = useLastCallback((clientX: number, clientY: number) => { const bounds = videoRef.current?.getBoundingClientRect(); if (!bounds) return; @@ -150,7 +155,7 @@ const VideoPlayer: FC = ({ const handleSeekingChange = useLastCallback((isSeeking: boolean) => { setIsSeeking(isSeeking); - if (!isSeeking) { + if (!isSeeking && lastMousePositionRef.current) { const { x, y } = lastMousePositionRef.current; checkMousePositionAndToggleControls(x, y); } @@ -250,7 +255,7 @@ const VideoPlayer: FC = ({ if (isLooped) return; setCurrentTime(0); setIsPlaying(false); - toggleControls(true); + toggleControls(true, true); }); const handleFullscreenChange = useLastCallback(() => { diff --git a/src/components/mediaViewer/hooks/useControlsSignal.ts b/src/components/mediaViewer/hooks/useControlsSignal.ts index eed6659e8..9144f2201 100644 --- a/src/components/mediaViewer/hooks/useControlsSignal.ts +++ b/src/components/mediaViewer/hooks/useControlsSignal.ts @@ -1,10 +1,37 @@ +import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment'; import { createSignal } from '../../../util/signals'; import useDerivedSignal from '../../../hooks/useDerivedSignal'; -const [getControlsVisible, setControlsVisible] = createSignal(false); +const [getControlsVisible, setControlsVisibleSignal] = createSignal(false); const [getIsLocked, setIsLocked] = createSignal(false); +let playerElement: HTMLElement | undefined; +let getMousePosition: (() => { x: number; y: number } | undefined) | undefined; + +function isMouseInsidePlayer() { + if (IS_TOUCH_ENV) return true; + if (!playerElement || !getMousePosition) return false; + const pos = getMousePosition(); + if (!pos) return true; + const bounds = playerElement.getBoundingClientRect(); + return pos.x >= bounds.left && pos.x <= bounds.right + && pos.y >= bounds.top && pos.y <= bounds.bottom; +} + +const setControlsVisible = (value: boolean, noPositionCheck?: boolean) => { + if (value && (!noPositionCheck && !isMouseInsidePlayer())) return; + setControlsVisibleSignal(value); +}; + +export function registerPlayerElement( + el: HTMLElement | undefined, + mousePositionGetter?: () => { x: number; y: number } | undefined, +) { + playerElement = el; + getMousePosition = mousePositionGetter; +} + export default function useControlsSignal() { const getVisible = useDerivedSignal( () => getControlsVisible() && !getIsLocked(),