mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 08:21:51 +01:00
fixed positioning and added loading icon
This commit is contained in:
parent
1ade111757
commit
4a36f98bf4
@ -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);
|
||||||
|
@ -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: "" };
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user