refactor: 重构作品播放页性能、媒体同步与内存管理
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,74 +1,90 @@
|
||||
import { useEffect } from "react";
|
||||
import type { RefObject } from "react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
interface UseBackgroundCanvasProps {
|
||||
isVideo: boolean;
|
||||
idx: number;
|
||||
isPlaying: boolean;
|
||||
videoRef: RefObject<HTMLVideoElement | null>;
|
||||
scrollerRef: RefObject<HTMLDivElement | null>;
|
||||
backgroundCanvasRef: RefObject<HTMLCanvasElement | null>;
|
||||
}
|
||||
|
||||
const CANVAS_DRAW_INTERVAL_MS = 100; // 10fps 对 1/10 分辨率 + blur(40px) 已完全平滑,大幅降低 GPU 负载
|
||||
|
||||
export function useBackgroundCanvas({
|
||||
isVideo,
|
||||
idx,
|
||||
isPlaying,
|
||||
videoRef,
|
||||
scrollerRef,
|
||||
backgroundCanvasRef,
|
||||
}: UseBackgroundCanvasProps) {
|
||||
}: UseBackgroundCanvasProps): void {
|
||||
// 初始化尺寸与卸载清理
|
||||
useEffect(() => {
|
||||
const canvas = backgroundCanvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
if (!isVideo) {
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
ctx.clearRect(0, 0, 1, 1);
|
||||
if (!ctx || !isVideo) {
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const updateCanvasSize = () => {
|
||||
canvas.width = Math.floor(window.innerWidth / 10);
|
||||
canvas.height = Math.floor(window.innerHeight / 10);
|
||||
const updateCanvasSize = (): void => {
|
||||
canvas.width = Math.max(1, Math.floor(window.innerWidth / 10));
|
||||
canvas.height = Math.max(1, Math.floor(window.innerHeight / 10));
|
||||
};
|
||||
updateCanvasSize();
|
||||
|
||||
let resizeTimeout: NodeJS.Timeout;
|
||||
const debouncedResize = () => {
|
||||
clearTimeout(resizeTimeout);
|
||||
let resizeTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
const debouncedResize = (): void => {
|
||||
if (resizeTimeout !== null) clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(updateCanvasSize, 300);
|
||||
};
|
||||
window.addEventListener("resize", debouncedResize);
|
||||
|
||||
const drawMediaToCanvas = () => {
|
||||
if (!ctx) return;
|
||||
return () => {
|
||||
window.removeEventListener("resize", debouncedResize);
|
||||
if (resizeTimeout !== null) clearTimeout(resizeTimeout);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
};
|
||||
}, [isVideo, backgroundCanvasRef]);
|
||||
|
||||
// 仅在视频实际播放时进行低频采样绘制,暂停时不占 CPU/GPU
|
||||
useEffect(() => {
|
||||
if (!isVideo || !isPlaying) return;
|
||||
const canvas = backgroundCanvasRef.current;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
const drawMediaToCanvas = (): void => {
|
||||
if (document.hidden) return;
|
||||
const sourceElement = videoRef.current;
|
||||
|
||||
if (
|
||||
!sourceElement ||
|
||||
(sourceElement instanceof HTMLVideoElement &&
|
||||
sourceElement.readyState < 2)
|
||||
)
|
||||
sourceElement.readyState < 2 ||
|
||||
sourceElement.paused
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canvasWidth = canvas.width;
|
||||
const canvasHeight = canvas.height;
|
||||
const sourceWidth = sourceElement.videoWidth;
|
||||
const sourceHeight = sourceElement.videoHeight;
|
||||
|
||||
if (!sourceWidth || !sourceHeight) return;
|
||||
if (!canvasWidth || !canvasHeight || !sourceWidth || !sourceHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canvasRatio = canvasWidth / canvasHeight;
|
||||
const sourceRatio = sourceWidth / sourceHeight;
|
||||
|
||||
let drawWidth: number,
|
||||
drawHeight: number,
|
||||
offsetX: number,
|
||||
offsetY: number;
|
||||
let drawWidth: number;
|
||||
let drawHeight: number;
|
||||
let offsetX: number;
|
||||
let offsetY: number;
|
||||
|
||||
if (canvasRatio > sourceRatio) {
|
||||
drawWidth = canvasWidth;
|
||||
@@ -85,12 +101,14 @@ export function useBackgroundCanvas({
|
||||
ctx.drawImage(sourceElement, offsetX, offsetY, drawWidth, drawHeight);
|
||||
};
|
||||
|
||||
const intervalId = setInterval(drawMediaToCanvas, 34);
|
||||
drawMediaToCanvas();
|
||||
const intervalId = window.setInterval(
|
||||
drawMediaToCanvas,
|
||||
CANVAS_DRAW_INTERVAL_MS,
|
||||
);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
window.removeEventListener("resize", debouncedResize);
|
||||
clearTimeout(resizeTimeout);
|
||||
window.clearInterval(intervalId);
|
||||
};
|
||||
}, [isVideo, idx, videoRef, scrollerRef, backgroundCanvasRef]);
|
||||
}, [isVideo, isPlaying, videoRef, backgroundCanvasRef]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user