101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
import { useEffect } from "react";
|
|
import type { RefObject } from "react";
|
|
|
|
interface UseBackgroundCanvasProps {
|
|
isVideo: boolean;
|
|
idx: number;
|
|
videoRef: RefObject<HTMLVideoElement | null>;
|
|
scrollerRef: RefObject<HTMLDivElement | null>;
|
|
backgroundCanvasRef: RefObject<HTMLCanvasElement | null>;
|
|
}
|
|
|
|
export function useBackgroundCanvas({
|
|
isVideo,
|
|
idx,
|
|
videoRef,
|
|
scrollerRef,
|
|
backgroundCanvasRef,
|
|
}: UseBackgroundCanvasProps) {
|
|
useEffect(() => {
|
|
const canvas = backgroundCanvasRef.current;
|
|
if (!canvas) return;
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
if (!ctx) return;
|
|
|
|
const updateCanvasSize = () => {
|
|
canvas.width = Math.floor(window.innerWidth / 10);
|
|
canvas.height = Math.floor(window.innerHeight / 10);
|
|
};
|
|
updateCanvasSize();
|
|
|
|
let resizeTimeout: NodeJS.Timeout;
|
|
const debouncedResize = () => {
|
|
clearTimeout(resizeTimeout);
|
|
resizeTimeout = setTimeout(updateCanvasSize, 300);
|
|
};
|
|
window.addEventListener("resize", debouncedResize);
|
|
|
|
const drawMediaToCanvas = () => {
|
|
if (!ctx) return;
|
|
|
|
let sourceElement: HTMLVideoElement | HTMLImageElement | null = null;
|
|
|
|
if (isVideo) {
|
|
sourceElement = videoRef.current;
|
|
} else {
|
|
const scroller = scrollerRef.current;
|
|
if (scroller) {
|
|
// 虚拟滚动:查找所有图片容器,找到当前显示的那个
|
|
const containers = scroller.querySelectorAll<HTMLElement>('div[style*="translateX"]');
|
|
for (const container of containers) {
|
|
const img = container.querySelector("img, video");
|
|
if (img && container.style.transform.includes(`${idx * 100}%`)) {
|
|
sourceElement = img as HTMLImageElement | HTMLVideoElement;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!sourceElement || (sourceElement instanceof HTMLVideoElement && sourceElement.readyState < 2)) return;
|
|
|
|
const canvasWidth = canvas.width;
|
|
const canvasHeight = canvas.height;
|
|
const sourceWidth =
|
|
sourceElement instanceof HTMLVideoElement ? sourceElement.videoWidth : sourceElement.naturalWidth;
|
|
const sourceHeight =
|
|
sourceElement instanceof HTMLVideoElement ? sourceElement.videoHeight : sourceElement.naturalHeight;
|
|
|
|
if (!sourceWidth || !sourceHeight) return;
|
|
|
|
const canvasRatio = canvasWidth / canvasHeight;
|
|
const sourceRatio = sourceWidth / sourceHeight;
|
|
|
|
let drawWidth: number, drawHeight: number, offsetX: number, offsetY: number;
|
|
|
|
if (canvasRatio > sourceRatio) {
|
|
drawWidth = canvasWidth;
|
|
drawHeight = canvasWidth / sourceRatio;
|
|
offsetX = 0;
|
|
offsetY = (canvasHeight - drawHeight) / 2;
|
|
} else {
|
|
drawHeight = canvasHeight;
|
|
drawWidth = canvasHeight * sourceRatio;
|
|
offsetX = (canvasWidth - drawWidth) / 2;
|
|
offsetY = 0;
|
|
}
|
|
|
|
ctx.drawImage(sourceElement, offsetX, offsetY, drawWidth, drawHeight);
|
|
};
|
|
|
|
const intervalId = setInterval(drawMediaToCanvas, 34);
|
|
|
|
return () => {
|
|
clearInterval(intervalId);
|
|
window.removeEventListener("resize", debouncedResize);
|
|
clearTimeout(resizeTimeout);
|
|
};
|
|
}, [isVideo, idx, videoRef, scrollerRef, backgroundCanvasRef]);
|
|
}
|