mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 08:41: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;
|
||||
}
|
||||
|
||||
.roll-infinite {
|
||||
animation: roll 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes roll {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
|
@ -3,11 +3,14 @@ export interface Thumbnail {
|
||||
to: number;
|
||||
imgUrl: string;
|
||||
}
|
||||
export const SCALE_FACTOR = 0.1;
|
||||
export const SCALE_FACTOR = 1;
|
||||
export default async function* extractThumbnails(
|
||||
videoUrl: string,
|
||||
numThumbnails: number
|
||||
): 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");
|
||||
video.src = videoUrl;
|
||||
video.crossOrigin = "anonymous";
|
||||
@ -18,12 +21,8 @@ export default async function* extractThumbnails(
|
||||
video.addEventListener("error", reject);
|
||||
});
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
|
||||
canvas.height = video.videoHeight * 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) {
|
||||
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 (
|
||||
<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
|
||||
className="-my-3 flex h-8 items-center"
|
||||
onMouseDown={dragMouseDown}
|
||||
onTouchStart={dragMouseDown}
|
||||
@ -101,6 +103,9 @@ export function ProgressAction() {
|
||||
dragging ? "!scale-[400%] !opacity-100" : ""
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isThumbnailVisible ? (
|
||||
<ThumbnailAction
|
||||
parentRef={ref}
|
||||
@ -109,8 +114,5 @@ export function ProgressAction() {
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { RefObject } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { formatSeconds } from "@/utils/formatSeconds";
|
||||
import { SCALE_FACTOR } from "@/utils/thumbnailCreator";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { VideoProgressEvent } from "@/video/state/logic/progress";
|
||||
import { useSource } from "@/video/state/logic/source";
|
||||
|
||||
const THUMBNAIL_HEIGHT = 100;
|
||||
export default function ThumbnailAction({
|
||||
parentRef,
|
||||
hoverPosition,
|
||||
@ -18,44 +20,61 @@ export default function ThumbnailAction({
|
||||
const descriptor = useVideoPlayerDescriptor();
|
||||
const source = useSource(descriptor);
|
||||
if (!parentRef.current) return null;
|
||||
const offset =
|
||||
(document.getElementsByTagName("video")[0].videoWidth * SCALE_FACTOR) / 2;
|
||||
const videoEl = document.getElementsByTagName("video")[0];
|
||||
const aspectRatio = videoEl.videoWidth / videoEl.videoHeight;
|
||||
const rect = parentRef.current.getBoundingClientRect();
|
||||
|
||||
if (!rect.width) return null;
|
||||
const hoverPercent = (hoverPosition - rect.left) / rect.width;
|
||||
const hoverTime = videoTime.duration * hoverPercent;
|
||||
|
||||
const thumbnailWidth = THUMBNAIL_HEIGHT * aspectRatio;
|
||||
const pos = () => {
|
||||
const relativePosition = hoverPosition - rect.left;
|
||||
if (relativePosition <= offset) {
|
||||
return 0;
|
||||
if (relativePosition <= thumbnailWidth / 2) {
|
||||
return rect.left;
|
||||
}
|
||||
if (relativePosition >= rect.width - offset) {
|
||||
return rect.width - offset * 2;
|
||||
if (relativePosition >= rect.width - thumbnailWidth / 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 (
|
||||
<div>
|
||||
<div className="text-center">
|
||||
{!src ? (
|
||||
<div
|
||||
style={{
|
||||
left: `${pos()}px`,
|
||||
width: `${thumbnailWidth}px`,
|
||||
height: `${THUMBNAIL_HEIGHT}px`,
|
||||
}}
|
||||
className="absolute bottom-32 flex items-center justify-center rounded bg-black"
|
||||
>
|
||||
<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-10 rounded"
|
||||
src={
|
||||
source.source?.thumbnails.find(
|
||||
(x) => x.from < hoverTime && x.to > hoverTime
|
||||
)?.imgUrl
|
||||
}
|
||||
className="absolute bottom-32 rounded"
|
||||
src={src}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
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>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user