alternative clipping improvement

frame-group-perf
Ryan Di 1 year ago
parent d636abff79
commit 683b80ad2b

@ -77,7 +77,11 @@ import {
isEmbeddableOrLabel, isEmbeddableOrLabel,
createPlaceholderEmbeddableLabel, createPlaceholderEmbeddableLabel,
} from "../element/embeddable"; } from "../element/embeddable";
import { getTargetFrame, isElementInFrame } from "../frame"; import {
elementsAreInFrameBounds,
getTargetFrame,
isElementInFrame,
} from "../frame";
import "canvas-roundrect-polyfill"; import "canvas-roundrect-polyfill";
export const DEFAULT_SPACING = 2; export const DEFAULT_SPACING = 2;
@ -357,54 +361,6 @@ const renderLinearElementPointHighlight = (
context.restore(); context.restore();
}; };
const getContiguousElements = (
elements: readonly ExcalidrawElement[],
appState: StaticCanvasAppState,
) => {
const contiguousElementsArray: ExcalidrawElement[][] = [];
let previousFrame: ExcalidrawFrameElement | null = null;
const contiguousElements: ExcalidrawElement[] = [];
const isNewContiguous = (
element: ExcalidrawElement,
targetFrame: ExcalidrawFrameElement | null,
) => {
return (
// element going to be added to some frame or not in any frame
!element.frameId ||
// element in frame but is either going to be put to a different frame / removed
(element.frameId &&
appState.selectedElementIds[element.id] &&
appState.selectedElementsAreBeingDragged &&
(!targetFrame ||
isElementInFrame(element, elements, appState, targetFrame))) ||
// element in a different frame from previous element
element.frameId !== previousFrame?.id
);
};
for (const element of elements) {
const targetFrame = getTargetFrame(element, appState);
if (isNewContiguous(element, targetFrame)) {
if (contiguousElements.length > 0) {
contiguousElementsArray.push([...contiguousElements]);
contiguousElements.length = 0;
}
previousFrame = targetFrame;
}
contiguousElements.push(element);
}
if (contiguousElements.length > 0) {
contiguousElementsArray.push([...contiguousElements]);
}
return contiguousElementsArray;
};
const frameClip = ( const frameClip = (
frame: ExcalidrawFrameElement, frame: ExcalidrawFrameElement,
context: CanvasRenderingContext2D, context: CanvasRenderingContext2D,
@ -943,38 +899,6 @@ const _renderInteractiveScene = ({
}; };
}; };
const renderContiguousElements = (
rc: StaticSceneRenderConfig["rc"],
appState: StaticSceneRenderConfig["appState"],
renderConfig: StaticSceneRenderConfig["renderConfig"],
context: CanvasRenderingContext2D,
contiguousElements: ExcalidrawElement[],
) => {
const { isExporting } = renderConfig;
for (const element of contiguousElements) {
try {
renderElement(element, rc, context, renderConfig, appState);
if (
isEmbeddableElement(element) &&
(isExporting || !element.validated) &&
element.width &&
element.height
) {
const label = createPlaceholderEmbeddableLabel(element);
renderElement(label, rc, context, renderConfig, appState);
}
if (!isExporting) {
renderLinkIcon(element, context, appState);
}
} catch (error: any) {
console.error(error);
}
}
};
const _renderStaticScene = ({ const _renderStaticScene = ({
canvas, canvas,
rc, rc,
@ -1030,38 +954,58 @@ const _renderStaticScene = ({
isEmbeddableOrLabel(el), isEmbeddableOrLabel(el),
); );
const contiguousElementsArray = [ const visibleElementsToRender = [
...getContiguousElements(visibleNonEmbeddableOrLabelElements, appState), ...visibleNonEmbeddableOrLabelElements,
...getContiguousElements(visibleEmbeddableOrLabelElements, appState), ...visibleEmbeddableOrLabelElements,
]; ];
for (const contiguousElements of contiguousElementsArray) { const _renderElement = (element: ExcalidrawElement) => {
const firstElement = contiguousElements[0]; try {
renderElement(element, rc, context, renderConfig, appState);
if (firstElement) {
context.save();
const frameId = firstElement.frameId || appState.frameToHighlight?.id;
if ( if (
frameId && isEmbeddableElement(element) &&
appState.frameRendering.enabled && (isExporting || !element.validated) &&
appState.frameRendering.clip element.width &&
element.height
) { ) {
const frame = getTargetFrame(firstElement, appState); const label = createPlaceholderEmbeddableLabel(element);
renderElement(label, rc, context, renderConfig, appState);
}
if (frame && isElementInFrame(firstElement, elements, appState)) { if (!isExporting) {
frameClip(frame, context, renderConfig, appState); renderLinkIcon(element, context, appState);
}
} }
} catch (error: any) {
console.error(error);
}
};
renderContiguousElements( for (const element of visibleElementsToRender) {
rc, const frameId = element.frameId || appState.frameToHighlight?.id;
appState,
renderConfig, if (
context, frameId &&
contiguousElements, appState.frameRendering.enabled &&
); appState.frameRendering.clip
context.restore(); ) {
const targetFrame = getTargetFrame(element, appState);
// for perf:
// only clip elements that are not completely in the target frame
if (
targetFrame &&
!elementsAreInFrameBounds([element], targetFrame) &&
isElementInFrame(element, elements, appState)
) {
context.save();
frameClip(targetFrame, context, renderConfig, appState);
_renderElement(element);
context.restore();
} else {
_renderElement(element);
}
} else {
_renderElement(element);
} }
} }
}; };

Loading…
Cancel
Save