Browse Source

fix: proper canvas size on frame export

Arnošt Pleskot 2 years ago
parent
commit
0c4d6fbe95
1 changed files with 105 additions and 38 deletions
  1. 105 38
      src/scene/export.ts

+ 105 - 38
src/scene/export.ts

@@ -1,9 +1,13 @@
 import rough from "roughjs/bin/rough";
 import { NonDeletedExcalidrawElement } from "../element/types";
-import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
+import {
+  Bounds,
+  getCommonBounds,
+  getElementAbsoluteCoords,
+} from "../element/bounds";
 import { renderSceneToSvg, renderStaticScene } from "../renderer/renderScene";
 import {
-  convertToExportPadding,
+  convertToExportPadding as convertExportPadding,
   distance,
   expandToAspectRatio,
   isOnlyExportingSingleFrame,
@@ -61,19 +65,29 @@ export const exportToCanvas = async (
   );
 
   const padding = !exportingWithFancyBackground
-    ? convertToExportPadding(exportPadding)
+    ? convertExportPadding(exportPadding)
     : getFancyBackgroundPadding(
-        convertToExportPadding(exportPadding),
+        convertExportPadding(exportPadding),
         FANCY_BG_INCLUDE_LOGO,
       );
 
-  const onlyExportingSingleFrame =
-    isOnlyExportingSingleFrame(elements) && !exportingWithFancyBackground;
+  const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
 
   const [minX, minY, width, height] = !exportingWithFancyBackground
-    ? getCanvasSize(elements, padding, onlyExportingSingleFrame)
-    : getCanvasSize(elements, padding, onlyExportingSingleFrame, {
-        aspectRatio: { width: 16, height: 9 },
+    ? getCanvasSize({
+        elements,
+        padding,
+        onlyExportingSingleFrame,
+        exportingWithFancyBackground,
+      })
+    : getCanvasSize({
+        elements,
+        padding,
+        onlyExportingSingleFrame,
+        exportingWithFancyBackground,
+        opts: {
+          aspectRatio: { width: 16, height: 9 },
+        },
       });
 
   const { canvas, scale = 1 } = createCanvas(width, height);
@@ -95,7 +109,10 @@ export const exportToCanvas = async (
     exportingWithFancyBackground &&
     appState.fancyBackgroundImageKey !== "solid"
   ) {
-    const commonBounds = getCommonBounds(elements);
+    const commonBounds = getElementsSize({
+      elements,
+      onlyExportingSingleFrame,
+    });
     const contentSize: Dimensions = {
       width: distance(commonBounds[0], commonBounds[2]),
       height: distance(commonBounds[1], commonBounds[3]),
@@ -131,9 +148,15 @@ export const exportToCanvas = async (
           ? viewBackgroundColor
           : null,
       scrollX:
-        -minX + (onlyExportingSingleFrame ? 0 : padding[3] + scrollXAdjustment),
+        -minX +
+        (onlyExportingSingleFrame && !exportingWithFancyBackground
+          ? 0
+          : padding[3] + scrollXAdjustment),
       scrollY:
-        -minY + (onlyExportingSingleFrame ? 0 : padding[0] + scrollYAdjustment),
+        -minY +
+        (onlyExportingSingleFrame && !exportingWithFancyBackground
+          ? 0
+          : padding[0] + scrollYAdjustment),
       zoom: defaultAppState.zoom,
       shouldCacheIgnoreZoom: false,
       theme: appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT,
@@ -183,9 +206,9 @@ export const exportToSvg = async (
   );
 
   const padding = !exportingWithFancyBackground
-    ? convertToExportPadding(exportPadding)
+    ? convertExportPadding(exportPadding)
     : getFancyBackgroundPadding(
-        convertToExportPadding(exportPadding),
+        convertExportPadding(exportPadding),
         FANCY_BG_INCLUDE_LOGO,
       );
 
@@ -204,13 +227,23 @@ export const exportToSvg = async (
     }
   }
 
-  const onlyExportingSingleFrame =
-    isOnlyExportingSingleFrame(elements) && !exportingWithFancyBackground;
+  const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
 
   const [minX, minY, width, height] = !exportingWithFancyBackground
-    ? getCanvasSize(elements, padding, onlyExportingSingleFrame)
-    : getCanvasSize(elements, padding, onlyExportingSingleFrame, {
-        aspectRatio: { width: 16, height: 9 },
+    ? getCanvasSize({
+        elements,
+        padding,
+        onlyExportingSingleFrame,
+        exportingWithFancyBackground,
+      })
+    : getCanvasSize({
+        elements,
+        padding,
+        onlyExportingSingleFrame,
+        exportingWithFancyBackground,
+        opts: {
+          aspectRatio: { width: 16, height: 9 },
+        },
       });
 
   // initialize SVG root
@@ -244,8 +277,16 @@ export const exportToSvg = async (
     Scene.getScene(elements[0])?.getNonDeletedElements()?.length ===
     elements.length;
 
-  const offsetX = -minX + (onlyExportingSingleFrame ? 0 : padding[3]);
-  const offsetY = -minY + (onlyExportingSingleFrame ? 0 : padding[0]);
+  const offsetX =
+    -minX +
+    (onlyExportingSingleFrame && !exportingWithFancyBackground
+      ? 0
+      : padding[3]);
+  const offsetY =
+    -minY +
+    (onlyExportingSingleFrame && !exportingWithFancyBackground
+      ? 0
+      : padding[0]);
 
   const exportingFrame =
     isExportingWholeCanvas || !onlyExportingSingleFrame
@@ -297,7 +338,10 @@ export const exportToSvg = async (
       appState.fancyBackgroundImageKey &&
       appState.fancyBackgroundImageKey !== "solid"
     ) {
-      const commonBounds = getCommonBounds(elements);
+      const commonBounds = getElementsSize({
+        elements,
+        onlyExportingSingleFrame,
+      });
       const contentSize: Dimensions = {
         width: distance(commonBounds[0], commonBounds[2]),
         height: distance(commonBounds[1], commonBounds[3]),
@@ -342,17 +386,16 @@ export const exportToSvg = async (
   return svgRoot;
 };
 
-// calculate smallest area to fit the contents in
-const getCanvasSize = (
-  elements: readonly NonDeletedExcalidrawElement[],
-  exportPadding: ExportPadding,
-  onlyExportingSingleFrame: boolean,
-  opts?: { aspectRatio: Dimensions },
-): [number, number, number, number] => {
+const getElementsSize = ({
+  elements,
+  onlyExportingSingleFrame,
+}: {
+  elements: readonly NonDeletedExcalidrawElement[];
+  onlyExportingSingleFrame: boolean;
+}): Bounds => {
   // we should decide if we are exporting the whole canvas
   // if so, we are not clipping elements in the frame
   // and therefore, we should not do anything special
-
   const isExportingWholeCanvas =
     Scene.getScene(elements[0])?.getNonDeletedElements()?.length ===
     elements.length;
@@ -372,17 +415,37 @@ const getCanvasSize = (
     );
   }
 
-  const [minX, minY, maxX, maxY] = getCommonBounds(elements);
+  return getCommonBounds(elements);
+};
+
+// calculate smallest area to fit the contents in
+const getCanvasSize = ({
+  elements,
+  padding,
+  onlyExportingSingleFrame,
+  exportingWithFancyBackground,
+  opts,
+}: {
+  elements: readonly NonDeletedExcalidrawElement[];
+  padding: ExportPadding;
+  onlyExportingSingleFrame: boolean;
+  exportingWithFancyBackground: boolean;
+  opts?: { aspectRatio: Dimensions };
+}): [number, number, number, number] => {
+  const [minX, minY, maxX, maxY] = getElementsSize({
+    elements,
+    onlyExportingSingleFrame,
+  });
 
   let width = 0;
   let height = 0;
 
-  if (onlyExportingSingleFrame) {
+  if (onlyExportingSingleFrame && !exportingWithFancyBackground) {
     width = distance(minX, maxX);
     height = distance(minY, maxY);
   } else {
-    width = distance(minX, maxX) + exportPadding[1] + exportPadding[3];
-    height = distance(minY, maxY) + exportPadding[0] + exportPadding[2];
+    width = distance(minX, maxX) + padding[1] + padding[3];
+    height = distance(minY, maxY) + padding[0] + padding[2];
   }
 
   if (opts?.aspectRatio) {
@@ -403,11 +466,15 @@ export const getExportSize = (
   scale: number,
   appState: AppState,
 ): [number, number] => {
-  const [, , width, height] = getCanvasSize(
+  const [, , width, height] = getCanvasSize({
     elements,
-    convertToExportPadding(exportPadding),
-    isExportingWithFacnyBackground(appState, elements),
-  ).map((dimension) => Math.trunc(dimension * scale));
+    padding: convertExportPadding(exportPadding),
+    onlyExportingSingleFrame: isOnlyExportingSingleFrame(elements),
+    exportingWithFancyBackground: isExportingWithFacnyBackground(
+      appState,
+      elements,
+    ),
+  }).map((dimension) => Math.trunc(dimension * scale));
 
   return [width, height];
 };