Browse Source

alternative clipping improvement

Ryan Di 1 year ago
parent
commit
683b80ad2b
1 changed files with 49 additions and 105 deletions
  1. 49 105
      src/renderer/renderScene.ts

+ 49 - 105
src/renderer/renderScene.ts

@@ -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 = [
-    ...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 (
       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);
     }
     }
   }
   }
 };
 };