You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import React, { useEffect, useRef } from "react";
|
|
import type { RoughCanvas } from "roughjs/bin/canvas";
|
|
import { renderStaticScene } from "../../renderer/staticScene";
|
|
import { isShallowEqual } from "../../utils";
|
|
import type { AppState, StaticCanvasAppState } from "../../types";
|
|
import type {
|
|
RenderableElementsMap,
|
|
StaticCanvasRenderConfig,
|
|
} from "../../scene/types";
|
|
import type {
|
|
NonDeletedExcalidrawElement,
|
|
NonDeletedSceneElementsMap,
|
|
} from "../../element/types";
|
|
import { isRenderThrottlingEnabled } from "../../reactUtils";
|
|
|
|
type StaticCanvasProps = {
|
|
canvas: HTMLCanvasElement;
|
|
rc: RoughCanvas;
|
|
elementsMap: RenderableElementsMap;
|
|
allElementsMap: NonDeletedSceneElementsMap;
|
|
visibleElements: readonly NonDeletedExcalidrawElement[];
|
|
sceneNonce: number | undefined;
|
|
selectionNonce: number | undefined;
|
|
scale: number;
|
|
appState: StaticCanvasAppState;
|
|
renderConfig: StaticCanvasRenderConfig;
|
|
};
|
|
|
|
const StaticCanvas = (props: StaticCanvasProps) => {
|
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
const isComponentMounted = useRef(false);
|
|
|
|
useEffect(() => {
|
|
const wrapper = wrapperRef.current;
|
|
if (!wrapper) {
|
|
return;
|
|
}
|
|
|
|
const canvas = props.canvas;
|
|
|
|
if (!isComponentMounted.current) {
|
|
isComponentMounted.current = true;
|
|
|
|
wrapper.replaceChildren(canvas);
|
|
canvas.classList.add("excalidraw__canvas", "static");
|
|
}
|
|
|
|
const widthString = `${props.appState.width}px`;
|
|
const heightString = `${props.appState.height}px`;
|
|
if (canvas.style.width !== widthString) {
|
|
canvas.style.width = widthString;
|
|
}
|
|
if (canvas.style.height !== heightString) {
|
|
canvas.style.height = heightString;
|
|
}
|
|
|
|
const scaledWidth = props.appState.width * props.scale;
|
|
const scaledHeight = props.appState.height * props.scale;
|
|
// setting width/height resets the canvas even if dimensions not changed,
|
|
// which would cause flicker when we skip frame (due to throttling)
|
|
if (canvas.width !== scaledWidth) {
|
|
canvas.width = scaledWidth;
|
|
}
|
|
if (canvas.height !== scaledHeight) {
|
|
canvas.height = scaledHeight;
|
|
}
|
|
|
|
renderStaticScene(
|
|
{
|
|
canvas,
|
|
rc: props.rc,
|
|
scale: props.scale,
|
|
elementsMap: props.elementsMap,
|
|
allElementsMap: props.allElementsMap,
|
|
visibleElements: props.visibleElements,
|
|
appState: props.appState,
|
|
renderConfig: props.renderConfig,
|
|
},
|
|
isRenderThrottlingEnabled(),
|
|
);
|
|
});
|
|
|
|
return <div className="excalidraw__canvas-wrapper" ref={wrapperRef} />;
|
|
};
|
|
|
|
const getRelevantAppStateProps = (
|
|
appState: AppState,
|
|
): StaticCanvasAppState => ({
|
|
zoom: appState.zoom,
|
|
scrollX: appState.scrollX,
|
|
scrollY: appState.scrollY,
|
|
width: appState.width,
|
|
height: appState.height,
|
|
viewModeEnabled: appState.viewModeEnabled,
|
|
offsetLeft: appState.offsetLeft,
|
|
offsetTop: appState.offsetTop,
|
|
theme: appState.theme,
|
|
pendingImageElementId: appState.pendingImageElementId,
|
|
shouldCacheIgnoreZoom: appState.shouldCacheIgnoreZoom,
|
|
viewBackgroundColor: appState.viewBackgroundColor,
|
|
exportScale: appState.exportScale,
|
|
selectedElementsAreBeingDragged: appState.selectedElementsAreBeingDragged,
|
|
gridSize: appState.gridSize,
|
|
frameRendering: appState.frameRendering,
|
|
selectedElementIds: appState.selectedElementIds,
|
|
frameToHighlight: appState.frameToHighlight,
|
|
editingGroupId: appState.editingGroupId,
|
|
});
|
|
|
|
const areEqual = (
|
|
prevProps: StaticCanvasProps,
|
|
nextProps: StaticCanvasProps,
|
|
) => {
|
|
if (
|
|
prevProps.sceneNonce !== nextProps.sceneNonce ||
|
|
prevProps.scale !== nextProps.scale ||
|
|
// we need to memoize on elementsMap because they may have renewed
|
|
// even if sceneNonce didn't change (e.g. we filter elements out based
|
|
// on appState)
|
|
prevProps.elementsMap !== nextProps.elementsMap ||
|
|
prevProps.visibleElements !== nextProps.visibleElements
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return (
|
|
isShallowEqual(
|
|
// asserting AppState because we're being passed the whole AppState
|
|
// but resolve to only the StaticCanvas-relevant props
|
|
getRelevantAppStateProps(prevProps.appState as AppState),
|
|
getRelevantAppStateProps(nextProps.appState as AppState),
|
|
) && isShallowEqual(prevProps.renderConfig, nextProps.renderConfig)
|
|
);
|
|
};
|
|
|
|
export default React.memo(StaticCanvas, areEqual);
|