From 683b80ad2b33f792c383c87a9ccb6a024c1ce579 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Tue, 14 Nov 2023 18:31:21 +0800 Subject: [PATCH] alternative clipping improvement --- src/renderer/renderScene.ts | 156 ++++++++++++------------------------ 1 file changed, 50 insertions(+), 106 deletions(-) diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index e18e5aa64..a4afeda60 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -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); - - if (frame && isElementInFrame(firstElement, elements, appState)) { - frameClip(frame, context, renderConfig, appState); - } + const label = createPlaceholderEmbeddableLabel(element); + renderElement(label, rc, context, renderConfig, appState); } - renderContiguousElements( - rc, - appState, - renderConfig, - context, - contiguousElements, - ); - context.restore(); + if (!isExporting) { + renderLinkIcon(element, context, appState); + } + } catch (error: any) { + console.error(error); + } + }; + + 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); } } };