smooth and optimized the play cursor, play cursor done

less files
This commit is contained in:
wil committed 2024-09-11 14:42:19 -04:00
1 parent b02004d90b
commit eff3c66821
8 files changed
+164 -82

No files matched your search

@@ -1,4 +1,4 @@
import { useCallback, useRef } from "react";
import { useCallback, useState } from "react";
import { VideoControls } from "./VideoControls";
import { TrimmerPlaybar } from "./TrimmerPlaybar";
@@ -13,14 +13,15 @@ export const QuickTrimVideoPlayer = ({ file }: { file: File }) => {
// trimStartMs: undefined,
// trimEndMs: undefined,
// });
const [vidEl, setVidEl] = useState<HTMLVideoElement | undefined>(undefined);
const videoRef = useRef<HTMLVideoElement | undefined>(undefined);
// const videoRef = useRef<HTMLVideoElement | undefined>(undefined);
const videoRefCallback = useCallback(
(node: HTMLVideoElement) => {
if (node !== null) {
// DOM node referenced by ref has changed and exists
videoRef.current = node;
setVidEl(node);
}
},
[
@@ -41,8 +42,8 @@ export const QuickTrimVideoPlayer = ({ file }: { file: File }) => {
/>
</div>
<div className="flex items-center justify-center">
<VideoControls vidEl={videoRef.current} className="w-fit" />
<TrimmerPlaybar vidEl={videoRef.current} className="grow" />
<VideoControls vidEl={vidEl} className="w-fit" />
<TrimmerPlaybar vidEl={vidEl} className="grow" />
</div>
</>
);
@@ -0,0 +1,95 @@
import { useCallback, useState } from "react";
import { twMerge } from "tailwind-merge";
import {
makePositionCalculator,
buttonStyles,
verticalPositionStyles,
} from "./utilities";
export const ProgressCursor = ({
progress,
vidEl,
}: {
progress: number;
vidEl: HTMLVideoElement;
}) => {
const [state, setState] = useState<{
isScrubbing: boolean;
scrubbingPosition: number;
}>({
isScrubbing: false,
scrubbingPosition: 0,
});
const { isScrubbing, scrubbingPosition } = state;
const handleScrubbingCurrentTime = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
const calcPosition = makePositionCalculator(e);
const wasPlaying = !vidEl.paused;
if (wasPlaying) {
vidEl.pause();
}
setState({
isScrubbing: true,
scrubbingPosition: calcPosition(e),
});
const handleMouseMove = (e: MouseEvent) => {
e.stopPropagation();
e.preventDefault();
const newPosition = calcPosition(e);
vidEl.currentTime = (newPosition / 100) * vidEl.duration;
setState({
isScrubbing: true,
scrubbingPosition: newPosition,
});
};
const handleMouseUp = () => {
setState((prev) => {
if (wasPlaying) {
vidEl.play();
}
return {
...prev,
isScrubbing: false,
};
});
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
},
[vidEl],
);
return (
<>
<div
// play progress bar
className="mt-3 h-4 bg-primary-500"
style={{
width: isScrubbing ? `${scrubbingPosition}%` : `${progress}%`,
}}
/>
<div
// current time scrubber
className={twMerge(
verticalPositionStyles,
buttonStyles,
"size-5 -translate-x-1/2 rounded-full",
isScrubbing && "cursor-grabbing",
)}
onMouseDown={handleScrubbingCurrentTime}
style={{ left: isScrubbing ? `${scrubbingPosition}%` : `${progress}%` }}
/>
</>
);
};
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { twMerge } from "tailwind-merge";
import {
faBracketCurly,
@@ -6,32 +6,12 @@ import {
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const MAX_TRIM_DURATION = 6000;
import { TrimmingPlaybarLoading } from "./TrimmerPlaybarLoading";
import { MAX_TRIM_DURATION } from "./constants";
import { buttonStyles, verticalPositionStyles } from "./utilities";
import { ProgressCursor } from "./ProgressCursor";
export const TrimmerPlaybar = ({
vidEl,
className,
}: {
vidEl: HTMLVideoElement | undefined;
className?: string;
}) => {
if (!vidEl) {
return <TrimmingPlaybarLoading />;
}
return <TrimmerPlaybarCore vidEl={vidEl} className={className} />;
};
const TrimmingPlaybarLoading = ({ className }: { className?: string }) => {
return (
<div className={twMerge("relative h-10 w-full bg-gray-200", className)}>
<div
// dummy play progress bar
className="mt-3 h-4 w-full animate-pulse bg-secondary-500"
/>
</div>
);
};
const TrimmerPlaybarCore = ({
export const TrimmerPlaybarCore = ({
vidEl,
className,
}: {
@@ -49,6 +29,7 @@ const TrimmerPlaybarCore = ({
trimStartMs: undefined,
trimEndMs: undefined,
});
const { duration, currentTime, trimStartMs, trimEndMs } = states;
const setTrimStartMs = useCallback((value: number) => {
@@ -109,42 +90,6 @@ const TrimmerPlaybarCore = ({
};
}, [vidEl]);
const handleScrubbingCurrentTime = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
const wasPlaying = !vidEl.paused;
if (wasPlaying) {
vidEl.pause();
}
const startX = e.clientX;
const handleMouseMove = (e: MouseEvent) => {
e.stopPropagation();
e.preventDefault();
// const width = (e.target as HTMLDivElement).getBoundingClientRect()
// .width;
// const deltaX = e.clientX - startX;
// const newCurrentTime =
// (currentTime ?? 0) + (deltaX / width) * (duration ?? 1);
// vidEl.currentTime = newCurrentTime;
};
const handleMouseUp = () => {
if (wasPlaying) {
vidEl.play();
}
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
},
[vidEl],
);
const buttonStyles = "border border-ui-border bg-ui-panel cursor-grab";
const verticalPositionStyles = "absolute top-1/2 -translate-y-1/2";
if (!duration) {
return <TrimmingPlaybarLoading className={className} />;
}
@@ -152,20 +97,9 @@ const TrimmerPlaybarCore = ({
<div
className={twMerge("relative mx-4 h-10 w-full bg-gray-200", className)}
>
<div
// play progress bar
className="mt-3 h-4 bg-primary-500"
style={{ width: `${((currentTime ?? 0) / (duration ?? 1)) * 100}%` }}
/>
<div
// current time scrubber
className={twMerge(
verticalPositionStyles,
buttonStyles,
"size-5 -translate-x-1/2 rounded-full",
)}
onMouseDown={handleScrubbingCurrentTime}
style={{ left: `${((currentTime ?? 0) / (duration ?? 1)) * 100}%` }}
<ProgressCursor
vidEl={vidEl}
progress={((currentTime ?? 0) / (duration ?? 1)) * 100}
/>
<div
// trim start scrubber
@@ -0,0 +1,13 @@
import { twMerge } from "tailwind-merge";
export const TrimmingPlaybarLoading = ({
className,
}: {
className?: string;
}) => {
return (
<div className={twMerge("relative h-10 w-full bg-gray-200", className)}>
<div className="mt-3 h-4 w-full animate-pulse bg-secondary-500" />
</div>
);
};
@@ -0,0 +1,5 @@
export const MAX_TRIM_DURATION = 6000;
export enum MouseState {
IDLE = "idle",
DRAGGING = "dragging",
}
@@ -0,0 +1,15 @@
import { TrimmerPlaybarCore } from "./TrimmerPlaybarCore";
import { TrimmingPlaybarLoading } from "./TrimmerPlaybarLoading";
export const TrimmerPlaybar = ({
vidEl,
className,
}: {
vidEl: HTMLVideoElement | undefined;
className?: string;
}) => {
if (!vidEl) {
return <TrimmingPlaybarLoading />;
}
return <TrimmerPlaybarCore vidEl={vidEl} className={className} />;
};
@@ -0,0 +1,20 @@
export const buttonStyles = "border border-ui-border bg-ui-panel cursor-grab";
export const verticalPositionStyles = "absolute top-1/2 -translate-y-1/2";
export const makePositionCalculator = (
e: MouseEvent | React.MouseEvent<HTMLDivElement>,
) => {
const thisEl = e.target as HTMLDivElement;
const parentEl = thisEl.parentElement as HTMLDivElement;
const parentWidth = parentEl.getBoundingClientRect().width;
return (currE: MouseEvent | React.MouseEvent<HTMLDivElement>) => {
const result = ((currE.clientX - parentEl.offsetLeft) / parentWidth) * 100;
if (result < 0) {
return 0;
}
if (result > 100) {
return 100;
}
return result;
};
};
@@ -13,7 +13,6 @@ export function ButtonPlaypause({ vidEl }: { vidEl: HTMLVideoElement }) {
PlayPauseStatus.PAUSED,
);
const togglePlaypause = () => {
console.log("togglePlaypause", playpause);
if (playpause === PlayPauseStatus.PLAYING) {
vidEl.pause();
} else {