douyin-archive/app/aweme/[awemeId]/hooks/useBackgroundCanvas.ts
2025-10-23 10:30:43 +08:00

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, 20);
return () => {
clearInterval(intervalId);
window.removeEventListener("resize", debouncedResize);
clearTimeout(resizeTimeout);
};
}, [isVideo, idx, videoRef, scrollerRef, backgroundCanvasRef]);
}