fixed positioning and added loading icon

This commit is contained in:
frost768 2023-06-10 14:38:26 +03:00
parent 1ade111757
commit 4a36f98bf4
4 changed files with 63 additions and 39 deletions

View File

@ -34,6 +34,10 @@ body[data-no-select] {
animation: roll 1s; animation: roll 1s;
} }
.roll-infinite {
animation: roll 2s infinite;
}
@keyframes roll { @keyframes roll {
from { from {
transform: rotate(0deg); transform: rotate(0deg);

View File

@ -3,11 +3,14 @@ export interface Thumbnail {
to: number; to: number;
imgUrl: string; imgUrl: string;
} }
export const SCALE_FACTOR = 0.1; export const SCALE_FACTOR = 1;
export default async function* extractThumbnails( export default async function* extractThumbnails(
videoUrl: string, videoUrl: string,
numThumbnails: number numThumbnails: number
): AsyncGenerator<Thumbnail, Thumbnail> { ): AsyncGenerator<Thumbnail, Thumbnail> {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) return { from: -1, to: -1, imgUrl: "" };
const video = document.createElement("video"); const video = document.createElement("video");
video.src = videoUrl; video.src = videoUrl;
video.crossOrigin = "anonymous"; video.crossOrigin = "anonymous";
@ -18,12 +21,8 @@ export default async function* extractThumbnails(
video.addEventListener("error", reject); video.addEventListener("error", reject);
}); });
const canvas = document.createElement("canvas");
canvas.height = video.videoHeight * SCALE_FACTOR; canvas.height = video.videoHeight * SCALE_FACTOR;
canvas.width = video.videoWidth * SCALE_FACTOR; canvas.width = video.videoWidth * SCALE_FACTOR;
const ctx = canvas.getContext("2d");
if (!ctx) return { from: 0, to: 0, imgUrl: "" };
for (let i = 0; i <= numThumbnails; i += 1) { for (let i = 0; i <= numThumbnails; i += 1) {
const from = (i / (numThumbnails + 1)) * video.duration; const from = (i / (numThumbnails + 1)) * video.duration;
@ -48,5 +47,5 @@ export default async function* extractThumbnails(
}; };
} }
return { from: 0, to: 0, imgUrl: "" }; return { from: -1, to: -1, imgUrl: "" };
} }

View File

@ -70,9 +70,11 @@ export function ProgressAction() {
); );
return ( return (
<div className="group pointer-events-auto w-full cursor-pointer rounded-full px-2"> <div
ref={ref}
className="group pointer-events-auto w-full cursor-pointer rounded-full px-2"
>
<div <div
ref={ref}
className="-my-3 flex h-8 items-center" className="-my-3 flex h-8 items-center"
onMouseDown={dragMouseDown} onMouseDown={dragMouseDown}
onTouchStart={dragMouseDown} onTouchStart={dragMouseDown}
@ -101,16 +103,16 @@ export function ProgressAction() {
dragging ? "!scale-[400%] !opacity-100" : "" dragging ? "!scale-[400%] !opacity-100" : ""
}`} }`}
/> />
{isThumbnailVisible ? (
<ThumbnailAction
parentRef={ref}
videoTime={videoTime}
hoverPosition={hoverPosition}
/>
) : null}
</div> </div>
</div> </div>
</div> </div>
{isThumbnailVisible ? (
<ThumbnailAction
parentRef={ref}
videoTime={videoTime}
hoverPosition={hoverPosition}
/>
) : null}
</div> </div>
); );
} }

View File

@ -1,11 +1,13 @@
import { RefObject } from "react"; import { RefObject } from "react";
import { Icon, Icons } from "@/components/Icon";
import { formatSeconds } from "@/utils/formatSeconds"; import { formatSeconds } from "@/utils/formatSeconds";
import { SCALE_FACTOR } from "@/utils/thumbnailCreator"; import { SCALE_FACTOR } from "@/utils/thumbnailCreator";
import { useVideoPlayerDescriptor } from "@/video/state/hooks"; import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { VideoProgressEvent } from "@/video/state/logic/progress"; import { VideoProgressEvent } from "@/video/state/logic/progress";
import { useSource } from "@/video/state/logic/source"; import { useSource } from "@/video/state/logic/source";
const THUMBNAIL_HEIGHT = 100;
export default function ThumbnailAction({ export default function ThumbnailAction({
parentRef, parentRef,
hoverPosition, hoverPosition,
@ -18,44 +20,61 @@ export default function ThumbnailAction({
const descriptor = useVideoPlayerDescriptor(); const descriptor = useVideoPlayerDescriptor();
const source = useSource(descriptor); const source = useSource(descriptor);
if (!parentRef.current) return null; if (!parentRef.current) return null;
const offset = const videoEl = document.getElementsByTagName("video")[0];
(document.getElementsByTagName("video")[0].videoWidth * SCALE_FACTOR) / 2; const aspectRatio = videoEl.videoWidth / videoEl.videoHeight;
const rect = parentRef.current.getBoundingClientRect(); const rect = parentRef.current.getBoundingClientRect();
if (!rect.width) return null;
const hoverPercent = (hoverPosition - rect.left) / rect.width; const hoverPercent = (hoverPosition - rect.left) / rect.width;
const hoverTime = videoTime.duration * hoverPercent; const hoverTime = videoTime.duration * hoverPercent;
const thumbnailWidth = THUMBNAIL_HEIGHT * aspectRatio;
const pos = () => { const pos = () => {
const relativePosition = hoverPosition - rect.left; const relativePosition = hoverPosition - rect.left;
if (relativePosition <= offset) { if (relativePosition <= thumbnailWidth / 2) {
return 0; return rect.left;
} }
if (relativePosition >= rect.width - offset) { if (relativePosition >= rect.width - thumbnailWidth / 2) {
return rect.width - offset * 2; return rect.width + rect.left - thumbnailWidth;
} }
return relativePosition - offset; return relativePosition + rect.left - thumbnailWidth / 2;
}; };
const src = source.source?.thumbnails.find(
(x) => x.from < hoverTime && x.to > hoverTime
)?.imgUrl;
return ( return (
<div> <div className="text-center">
<img {!src ? (
style={{ <div
left: `${pos()}px`, style={{
}} left: `${pos()}px`,
className="absolute bottom-10 rounded" width: `${thumbnailWidth}px`,
src={ height: `${THUMBNAIL_HEIGHT}px`,
source.source?.thumbnails.find( }}
(x) => x.from < hoverTime && x.to > hoverTime className="absolute bottom-32 flex items-center justify-center rounded bg-black"
)?.imgUrl >
} <Icon
/> className="roll-infinite text-6xl text-bink-600"
icon={Icons.MOVIE_WEB}
/>
</div>
) : (
<img
height={THUMBNAIL_HEIGHT}
width={thumbnailWidth}
style={{
left: `${pos()}px`,
}}
className="absolute bottom-32 rounded"
src={src}
/>
)}
<div <div
style={{ style={{
left: `${pos() + offset - 18}px`, left: `${pos() + thumbnailWidth / 2 - 18}px`,
}} }}
className="absolute bottom-3 text-white" className="absolute bottom-24 text-white"
> >
{formatSeconds(hoverTime)} {formatSeconds(hoverTime, videoEl.duration > 60 * 60)}
</div> </div>
</div> </div>
); );