diff --git a/src/components/video/DecoratedVideoPlayer.tsx b/src/components/video/DecoratedVideoPlayer.tsx new file mode 100644 index 00000000..8c1af036 --- /dev/null +++ b/src/components/video/DecoratedVideoPlayer.tsx @@ -0,0 +1,24 @@ +import { BackdropControl } from "./controls/BackdropControl"; +import { FullscreenControl } from "./controls/FullscreenControl"; +import { LoadingControl } from "./controls/LoadingControl"; +import { PauseControl } from "./controls/PauseControl"; +import { ProgressControl } from "./controls/ProgressControl"; +import { TimeControl } from "./controls/TimeControl"; +import { VolumeControl } from "./controls/VolumeControl"; +import { VideoPlayer, VideoPlayerProps } from "./VideoPlayer"; + +export function DecoratedVideoPlayer(props: VideoPlayerProps) { + return ( + + + + + + + + + + {props.children} + + ); +} diff --git a/src/components/video/VideoPlayer.tsx b/src/components/video/VideoPlayer.tsx index 8857d9fa..4222365d 100644 --- a/src/components/video/VideoPlayer.tsx +++ b/src/components/video/VideoPlayer.tsx @@ -1,7 +1,7 @@ import { forwardRef, useContext, useRef } from "react"; import { VideoPlayerContext, VideoPlayerContextProvider } from "./VideoContext"; -interface VideoPlayerProps { +export interface VideoPlayerProps { autoPlay?: boolean; children?: React.ReactNode; } diff --git a/src/components/video/controls/BackdropControl.tsx b/src/components/video/controls/BackdropControl.tsx new file mode 100644 index 00000000..3629c86e --- /dev/null +++ b/src/components/video/controls/BackdropControl.tsx @@ -0,0 +1,53 @@ +import { useCallback, useRef, useState } from "react"; +import { useVideoPlayerState } from "../VideoContext"; + +interface BackdropControlProps { + children?: React.ReactNode; +} + +export function BackdropControl(props: BackdropControlProps) { + const { videoState } = useVideoPlayerState(); + const [moved, setMoved] = useState(false); + const timeout = useRef | null>(null); + + const handleMouseMove = useCallback(() => { + setMoved(true); + if (timeout.current) clearTimeout(timeout.current); + timeout.current = setTimeout(() => { + setMoved(false); + timeout.current = null; + }, 3000); + }, [timeout, setMoved]); + + const handleClick = useCallback(() => { + if (videoState.isPlaying) videoState.pause(); + else videoState.play(); + }, [videoState]); + + const showUI = moved || videoState.isPaused; + + return ( +
+
+
+
+
{showUI ? props.children : null}
+
+ ); +} diff --git a/src/components/video/controls/ProgressControl.tsx b/src/components/video/controls/ProgressControl.tsx index 98c0ee16..4d423845 100644 --- a/src/components/video/controls/ProgressControl.tsx +++ b/src/components/video/controls/ProgressControl.tsx @@ -1,34 +1,61 @@ -import { useCallback, useRef } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useVideoPlayerState } from "../VideoContext"; export function ProgressControl() { const { videoState } = useVideoPlayerState(); const ref = useRef(null); + const [mouseDown, setMouseDown] = useState(false); + const [progress, setProgress] = useState(0); - const watchProgress = `${( + let watchProgress = `${( (videoState.time / videoState.duration) * 100 ).toFixed(2)}%`; + if (mouseDown) watchProgress = `${progress}%`; + const bufferProgress = `${( (videoState.buffered / videoState.duration) * 100 ).toFixed(2)}%`; - const handleClick = useCallback( - (e: React.MouseEvent) => { + useEffect(() => { + function mouseMove(ev: MouseEvent) { + if (!mouseDown || !ref.current) return; + const rect = ref.current.getBoundingClientRect(); + const pos = ((ev.pageX - rect.left) / ref.current.offsetWidth) * 100; + setProgress(pos); + } + + function mouseUp(ev: MouseEvent) { + if (!mouseDown) return; + setMouseDown(false); + document.body.removeAttribute("data-no-select"); + if (!ref.current) return; const rect = ref.current.getBoundingClientRect(); - const pos = (e.pageX - rect.left) / ref.current.offsetWidth; + const pos = (ev.pageX - rect.left) / ref.current.offsetWidth; videoState.setTime(pos * videoState.duration); - }, - [videoState, ref] - ); + } + + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUp); + + return () => { + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseup", mouseUp); + }; + }, [mouseDown, videoState]); + + const handleMouseDown = useCallback(() => { + setMouseDown(true); + document.body.setAttribute("data-no-select", "true"); + }, []); return (
{ setShow((v) => !v); }, [setShow]); @@ -33,15 +27,9 @@ export function TestView() { return (
- - - - - - - + - +
); }