perf: tidy frame bg render pass
This commit is contained in:
@@ -26,6 +26,7 @@ import { getElementAbsoluteCoords } from "@excalidraw/element";
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
|
ExcalidrawFrameElement,
|
||||||
ExcalidrawFrameLikeElement,
|
ExcalidrawFrameLikeElement,
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
} from "@excalidraw/element/types";
|
} from "@excalidraw/element/types";
|
||||||
@@ -278,39 +279,15 @@ const _renderStaticScene = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const groupsToBeAddedToFrame = new Set<string>();
|
const groupsToBeAddedToFrame = new Set<string>();
|
||||||
const renderedFrameBackgrounds = new Set<string>();
|
// iframe-like elements are rendered in a separate top-layer pass.
|
||||||
|
const nonIframeVisibleElements = visibleElements.filter(
|
||||||
const maybeRenderFrameBackground = (
|
(el) => !isIframeLikeElement(el),
|
||||||
element: NonDeletedExcalidrawElement | ExcalidrawFrameLikeElement,
|
);
|
||||||
) => {
|
// Frame background to render right before a given element id.
|
||||||
if (
|
const frameBackgroundByElementId = new Map<
|
||||||
!appState.frameRendering.enabled ||
|
NonDeletedExcalidrawElement["id"],
|
||||||
(!appState.frameRendering.outline && !renderConfig.exportingFrame)
|
ExcalidrawFrameElement
|
||||||
) {
|
>();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const frame =
|
|
||||||
renderConfig.exportingFrame ||
|
|
||||||
(isFrameLikeElement(element)
|
|
||||||
? element
|
|
||||||
: getContainingFrame(element, elementsMap));
|
|
||||||
|
|
||||||
if (!isFrameElement(frame)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frame || renderedFrameBackgrounds.has(frame.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderFrameBackground(frame, context, appState, {
|
|
||||||
roundCorners:
|
|
||||||
!renderConfig.exportingFrame ||
|
|
||||||
frame.id !== renderConfig.exportingFrame.id,
|
|
||||||
});
|
|
||||||
renderedFrameBackgrounds.add(frame.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
visibleElements.forEach((element) => {
|
visibleElements.forEach((element) => {
|
||||||
if (
|
if (
|
||||||
@@ -332,100 +309,135 @@ const _renderStaticScene = ({
|
|||||||
|
|
||||||
const inFrameGroupsMap = new Map<string, boolean>();
|
const inFrameGroupsMap = new Map<string, boolean>();
|
||||||
|
|
||||||
if (renderConfig.exportingFrame) {
|
if (
|
||||||
maybeRenderFrameBackground(renderConfig.exportingFrame);
|
appState.frameRendering.enabled &&
|
||||||
|
(appState.frameRendering.outline || renderConfig.exportingFrame)
|
||||||
|
) {
|
||||||
|
// Precompute where each frame background should be emitted to avoid
|
||||||
|
// re-resolving containing frames during the paint loop.
|
||||||
|
const renderedFrameBackgrounds = new Set<string>();
|
||||||
|
if (
|
||||||
|
renderConfig.exportingFrame &&
|
||||||
|
isFrameElement(renderConfig.exportingFrame)
|
||||||
|
) {
|
||||||
|
renderFrameBackground(renderConfig.exportingFrame, context, appState, {
|
||||||
|
roundCorners: false,
|
||||||
|
});
|
||||||
|
renderedFrameBackgrounds.add(renderConfig.exportingFrame.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const maybeQueueFrameBackground = (
|
||||||
|
element: NonDeletedExcalidrawElement | ExcalidrawFrameLikeElement,
|
||||||
|
) => {
|
||||||
|
const frame = isFrameLikeElement(element)
|
||||||
|
? element
|
||||||
|
: getContainingFrame(element, elementsMap);
|
||||||
|
|
||||||
|
if (!isFrameElement(frame) || renderedFrameBackgrounds.has(frame.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frameBackgroundByElementId.set(element.id, frame);
|
||||||
|
renderedFrameBackgrounds.add(frame.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
nonIframeVisibleElements.forEach((element) => {
|
||||||
|
maybeQueueFrameBackground(element);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paint visible elements
|
// Paint visible elements
|
||||||
visibleElements
|
nonIframeVisibleElements.forEach((element) => {
|
||||||
.filter((el) => !isIframeLikeElement(el))
|
try {
|
||||||
.forEach((element) => {
|
const frameBackground = frameBackgroundByElementId.get(element.id);
|
||||||
try {
|
if (frameBackground) {
|
||||||
// TODO: optimize (currently we call this func for each element because
|
renderFrameBackground(frameBackground, context, appState, {
|
||||||
// children come before their frames and we neeed to render the frame
|
roundCorners:
|
||||||
// background at the bottom)
|
!renderConfig.exportingFrame ||
|
||||||
maybeRenderFrameBackground(element);
|
frameBackground.id !== renderConfig.exportingFrame.id,
|
||||||
const frameId = element.frameId || appState.frameToHighlight?.id;
|
});
|
||||||
|
}
|
||||||
|
const frameId = element.frameId || appState.frameToHighlight?.id;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isTextElement(element) &&
|
||||||
|
element.containerId &&
|
||||||
|
elementsMap.has(element.containerId)
|
||||||
|
) {
|
||||||
|
// will be rendered with the container
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
|
||||||
|
if (
|
||||||
|
frameId &&
|
||||||
|
appState.frameRendering.enabled &&
|
||||||
|
appState.frameRendering.clip
|
||||||
|
) {
|
||||||
|
const frame = getTargetFrame(element, elementsMap, appState);
|
||||||
if (
|
if (
|
||||||
isTextElement(element) &&
|
frame &&
|
||||||
element.containerId &&
|
shouldApplyFrameClip(
|
||||||
elementsMap.has(element.containerId)
|
|
||||||
) {
|
|
||||||
// will be rendered with the container
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.save();
|
|
||||||
|
|
||||||
if (
|
|
||||||
frameId &&
|
|
||||||
appState.frameRendering.enabled &&
|
|
||||||
appState.frameRendering.clip
|
|
||||||
) {
|
|
||||||
const frame = getTargetFrame(element, elementsMap, appState);
|
|
||||||
if (
|
|
||||||
frame &&
|
|
||||||
shouldApplyFrameClip(
|
|
||||||
element,
|
|
||||||
frame,
|
|
||||||
appState,
|
|
||||||
elementsMap,
|
|
||||||
inFrameGroupsMap,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
frameClip(frame, context, renderConfig, appState);
|
|
||||||
}
|
|
||||||
renderElement(
|
|
||||||
element,
|
element,
|
||||||
elementsMap,
|
frame,
|
||||||
allElementsMap,
|
|
||||||
rc,
|
|
||||||
context,
|
|
||||||
renderConfig,
|
|
||||||
appState,
|
appState,
|
||||||
);
|
|
||||||
} else {
|
|
||||||
renderElement(
|
|
||||||
element,
|
|
||||||
elementsMap,
|
elementsMap,
|
||||||
allElementsMap,
|
inFrameGroupsMap,
|
||||||
rc,
|
)
|
||||||
context,
|
) {
|
||||||
renderConfig,
|
frameClip(frame, context, renderConfig, appState);
|
||||||
appState,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
renderElement(
|
||||||
const boundTextElement = getBoundTextElement(element, elementsMap);
|
element,
|
||||||
if (boundTextElement) {
|
elementsMap,
|
||||||
renderElement(
|
allElementsMap,
|
||||||
boundTextElement,
|
rc,
|
||||||
elementsMap,
|
context,
|
||||||
allElementsMap,
|
renderConfig,
|
||||||
rc,
|
appState,
|
||||||
context,
|
);
|
||||||
renderConfig,
|
} else {
|
||||||
appState,
|
renderElement(
|
||||||
);
|
element,
|
||||||
}
|
elementsMap,
|
||||||
|
allElementsMap,
|
||||||
context.restore();
|
rc,
|
||||||
|
context,
|
||||||
if (!isExporting) {
|
renderConfig,
|
||||||
renderLinkIcon(element, context, appState, elementsMap);
|
appState,
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(
|
|
||||||
error,
|
|
||||||
element.id,
|
|
||||||
element.x,
|
|
||||||
element.y,
|
|
||||||
element.width,
|
|
||||||
element.height,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
const boundTextElement = getBoundTextElement(element, elementsMap);
|
||||||
|
if (boundTextElement) {
|
||||||
|
renderElement(
|
||||||
|
boundTextElement,
|
||||||
|
elementsMap,
|
||||||
|
allElementsMap,
|
||||||
|
rc,
|
||||||
|
context,
|
||||||
|
renderConfig,
|
||||||
|
appState,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
|
||||||
|
if (!isExporting) {
|
||||||
|
renderLinkIcon(element, context, appState, elementsMap);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(
|
||||||
|
error,
|
||||||
|
element.id,
|
||||||
|
element.x,
|
||||||
|
element.y,
|
||||||
|
element.width,
|
||||||
|
element.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// render embeddables on top
|
// render embeddables on top
|
||||||
visibleElements
|
visibleElements
|
||||||
|
|||||||
Reference in New Issue
Block a user