alternative clipping improvement

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

@ -77,7 +77,11 @@ import {
isEmbeddableOrLabel,
createPlaceholderEmbeddableLabel,
} from "../element/embeddable";
import { getTargetFrame, isElementInFrame } from "../frame";
import {
elementsAreInFrameBounds,
getTargetFrame,
isElementInFrame,
} from "../frame";
import "canvas-roundrect-polyfill";
export const DEFAULT_SPACING = 2;
@ -357,54 +361,6 @@ const renderLinearElementPointHighlight = (
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 = (
frame: ExcalidrawFrameElement,
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 = ({
canvas,
rc,
@ -1030,38 +954,58 @@ const _renderStaticScene = ({
isEmbeddableOrLabel(el),
);
const contiguousElementsArray = [
...getContiguousElements(visibleNonEmbeddableOrLabelElements, appState),
...getContiguousElements(visibleEmbeddableOrLabelElements, appState),
const visibleElementsToRender = [
...visibleNonEmbeddableOrLabelElements,
...visibleEmbeddableOrLabelElements,
];
for (const contiguousElements of contiguousElementsArray) {
const firstElement = contiguousElements[0];
if (firstElement) {
context.save();
const frameId = firstElement.frameId || appState.frameToHighlight?.id;
const _renderElement = (element: ExcalidrawElement) => {
try {
renderElement(element, rc, context, renderConfig, appState);
if (
frameId &&
appState.frameRendering.enabled &&
appState.frameRendering.clip
isEmbeddableElement(element) &&
(isExporting || !element.validated) &&
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)) {
frameClip(frame, context, renderConfig, appState);
}
if (!isExporting) {
renderLinkIcon(element, context, appState);
}
} catch (error: any) {
console.error(error);
}
};
renderContiguousElements(
rc,
appState,
renderConfig,
context,
contiguousElements,
);
context.restore();
for (const element of visibleElementsToRender) {
const frameId = element.frameId || appState.frameToHighlight?.id;
if (
frameId &&
appState.frameRendering.enabled &&
appState.frameRendering.clip
) {
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