perf(canvas): coalesce renderer redraws

This commit is contained in:
syntaxbullet
2026-07-04 15:27:20 +02:00
parent d9e5e05183
commit dfa2bffeda
3 changed files with 293 additions and 3 deletions

View File

@@ -1,6 +1,7 @@
import { useEffect, type RefObject } from "react";
import type { AppStore } from "@editor/store";
import { createRenderer } from "@renderer/index";
import { createRenderer, type RenderFrame } from "@renderer/index";
import { canvasRenderFramesEqual, selectCanvasRenderFrame } from "./renderFrame";
export function useCanvasRenderer(canvasRef: RefObject<HTMLCanvasElement | null>, store: AppStore) {
useEffect(() => {
@@ -8,14 +9,38 @@ export function useCanvasRenderer(canvasRef: RefObject<HTMLCanvasElement | null>
if (!canvas) return;
const renderer = createRenderer(canvas);
renderer.render(store.getState());
let renderedFrame = selectCanvasRenderFrame(store.getState());
let pendingFrame: RenderFrame | undefined;
let animationFrame: number | undefined;
const flushRender = () => {
animationFrame = undefined;
const frame = pendingFrame;
if (!frame) return;
pendingFrame = undefined;
renderedFrame = frame;
renderer.render(frame);
};
const scheduleRender = (frame: RenderFrame) => {
pendingFrame = frame;
if (animationFrame !== undefined) return;
animationFrame = requestAnimationFrame(flushRender);
};
scheduleRender(renderedFrame);
const unsubscribe = store.subscribe((state) => {
renderer.render(state);
const nextFrame = selectCanvasRenderFrame(state);
const currentFrame = pendingFrame ?? renderedFrame;
if (canvasRenderFramesEqual(currentFrame, nextFrame)) return;
scheduleRender(nextFrame);
});
return () => {
unsubscribe();
if (animationFrame !== undefined) cancelAnimationFrame(animationFrame);
renderer.dispose();
};
}, [canvasRef, store]);