mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
smooth and optimized the play cursor, play cursor done
less files
This commit is contained in:
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}%` }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+10
-76
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user