perf: improve new element drawing (#8340)

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Ryan Di 2024-08-24 02:27:57 +08:00 committed by GitHub
parent b5d7f5b4ba
commit 5e1ff7cafe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 749 additions and 495 deletions

View file

@ -1,6 +1,7 @@
import { isElementInViewport } from "../element/sizeHelpers";
import { isImageElement } from "../element/typeChecks";
import type {
ExcalidrawElement,
NonDeletedElementsMap,
NonDeletedExcalidrawElement,
} from "../element/types";
@ -64,11 +65,13 @@ export class Renderer {
const getRenderableElements = ({
elements,
editingElement,
editingTextElement,
newElementId,
pendingImageElementId,
}: {
elements: readonly NonDeletedExcalidrawElement[];
editingElement: AppState["editingElement"];
editingTextElement: AppState["editingTextElement"];
newElementId: ExcalidrawElement["id"] | undefined;
pendingImageElementId: AppState["pendingImageElementId"];
}) => {
const elementsMap = toBrandedType<RenderableElementsMap>(new Map());
@ -83,12 +86,16 @@ export class Renderer {
}
}
if (newElementId === element.id) {
continue;
}
// we don't want to render text element that's being currently edited
// (it's rendered on remote only)
if (
!editingElement ||
editingElement.type !== "text" ||
element.id !== editingElement.id
!editingTextElement ||
editingTextElement.type !== "text" ||
element.id !== editingTextElement.id
) {
elementsMap.set(element.id, element);
}
@ -105,7 +112,8 @@ export class Renderer {
scrollY,
height,
width,
editingElement,
editingTextElement,
newElementId,
pendingImageElementId,
// cache-invalidation nonce
sceneNonce: _sceneNonce,
@ -117,7 +125,10 @@ export class Renderer {
scrollY: AppState["scrollY"];
height: AppState["height"];
width: AppState["width"];
editingElement: AppState["editingElement"];
editingTextElement: AppState["editingTextElement"];
/** note: first render of newElement will always bust the cache
* (we'd have to prefilter elements outside of this function) */
newElementId: ExcalidrawElement["id"] | undefined;
pendingImageElementId: AppState["pendingImageElementId"];
sceneNonce: ReturnType<InstanceType<typeof Scene>["getSceneNonce"]>;
}) => {
@ -125,7 +136,8 @@ export class Renderer {
const elementsMap = getRenderableElements({
elements,
editingElement,
editingTextElement,
newElementId,
pendingImageElementId,
});

View file

@ -218,10 +218,15 @@ export const getSelectedElements = (
export const getTargetElements = (
elements: ElementsMapOrArray,
appState: Pick<AppState, "selectedElementIds" | "editingElement">,
appState: Pick<
AppState,
"selectedElementIds" | "editingTextElement" | "newElement"
>,
) =>
appState.editingElement
? [appState.editingElement]
appState.editingTextElement
? [appState.editingTextElement]
: appState.newElement
? [appState.newElement]
: getSelectedElements(elements, appState, {
includeBoundTextElement: true,
});

View file

@ -92,6 +92,17 @@ export type InteractiveSceneRenderConfig = {
callback: (data: RenderInteractiveSceneCallback) => void;
};
export type NewElementSceneRenderConfig = {
canvas: HTMLCanvasElement | null;
rc: RoughCanvas;
newElement: ExcalidrawElement | null;
elementsMap: RenderableElementsMap;
allElementsMap: NonDeletedSceneElementsMap;
scale: number;
appState: AppState;
renderConfig: StaticCanvasRenderConfig;
};
export type SceneScroll = {
scrollX: number;
scrollY: number;