Sfoglia il codice sorgente

feat: resize canvas to aspect ratio when exporting with fancy bcg

Arnošt Pleskot 2 anni fa
parent
commit
c49bf04801
3 ha cambiato i file con 76 aggiunte e 9 eliminazioni
  1. 5 1
      src/components/ImageExportDialog.tsx
  2. 25 7
      src/scene/export.ts
  3. 46 1
      src/utils.ts

+ 5 - 1
src/components/ImageExportDialog.tsx

@@ -186,7 +186,11 @@ const ImageExportModal = ({
       appState,
       files,
       exportPadding: DEFAULT_EXPORT_PADDING,
-      maxWidthOrHeight,
+      maxWidthOrHeight: !(
+        exportBackgroundImage !== "solid" && exportWithBackground
+      )
+        ? maxWidthOrHeight
+        : undefined,
     })
       .then((canvas) => {
         setRenderError(null);

+ 25 - 7
src/scene/export.ts

@@ -2,8 +2,12 @@ import rough from "roughjs/bin/rough";
 import { NonDeletedExcalidrawElement } from "../element/types";
 import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
 import { renderSceneToSvg, renderStaticScene } from "../renderer/renderScene";
-import { distance, isOnlyExportingSingleFrame } from "../utils";
-import { AppState, BinaryFiles } from "../types";
+import {
+  distance,
+  expandToAspectRatio,
+  isOnlyExportingSingleFrame,
+} from "../utils";
+import { AppState, BinaryFiles, Dimensions } from "../types";
 import {
   DEFAULT_EXPORT_PADDING,
   FANCY_BACKGROUND_IMAGES,
@@ -58,7 +62,11 @@ export const exportToCanvas = async (
     ? exportPadding
     : getFancyBackgroundPadding(exportPadding);
 
-  const [minX, minY, width, height] = getCanvasSize(elements, padding);
+  const [minX, minY, width, height] = !exportWithFancyBackground
+    ? getCanvasSize(elements, padding)
+    : getCanvasSize(elements, padding, {
+        aspectRatio: { width: 16, height: 9 },
+      });
 
   const { canvas, scale = 1 } = createCanvas(width, height);
 
@@ -285,6 +293,7 @@ export const exportToSvg = async (
 const getCanvasSize = (
   elements: readonly NonDeletedExcalidrawElement[],
   exportPadding: number,
+  opts?: { aspectRatio: Dimensions },
 ): [number, number, number, number] => {
   // we should decide if we are exporting the whole canvas
   // if so, we are not clipping elements in the frame
@@ -311,11 +320,20 @@ const getCanvasSize = (
     );
   }
 
+  const padding = onlyExportingSingleFrame ? 0 : exportPadding * 2;
+
   const [minX, minY, maxX, maxY] = getCommonBounds(elements);
-  const width =
-    distance(minX, maxX) + (onlyExportingSingleFrame ? 0 : exportPadding * 2);
-  const height =
-    distance(minY, maxY) + (onlyExportingSingleFrame ? 0 : exportPadding * 2);
+  const width = distance(minX, maxX) + padding;
+  const height = distance(minY, maxY) + padding;
+
+  if (opts?.aspectRatio) {
+    const expandedDimensions = expandToAspectRatio(
+      { width, height },
+      opts.aspectRatio,
+    );
+
+    return [minX, minY, expandedDimensions.width, expandedDimensions.height];
+  }
 
   return [minX, minY, width, height];
 };

+ 46 - 1
src/utils.ts

@@ -16,7 +16,7 @@ import {
   FontString,
   NonDeletedExcalidrawElement,
 } from "./element/types";
-import { AppState, DataURL, LastActiveTool, Zoom } from "./types";
+import { AppState, DataURL, Dimensions, LastActiveTool, Zoom } from "./types";
 import { unstable_batchedUpdates } from "react-dom";
 import { SHAPES } from "./shapes";
 import { isEraserActive, isHandToolActive } from "./appState";
@@ -1007,3 +1007,48 @@ export const isRenderThrottlingEnabled = (() => {
 export const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
   ? devicePixelRatio
   : 1;
+
+/**
+ * Expands dimensions to fit into a specified aspect ratio without cropping.
+ * The resulting dimensions are rounded up to the nearest integer.
+ *
+ * @param dimensions - The original dimensions.
+ * @param aspectRatio - The aspect ratio to fit the dimensions into.
+ *
+ * @return The expanded dimensions.
+ *
+ * @example
+ * ```typescript
+ * const originalDimensions = { width: 800, height: 600 };
+ * const targetAspectRatio = { width: 16, height: 9 };
+ * const expandedDimensions = expandToAspectRatio(originalDimensions, targetAspectRatio);
+ * // Output will be { width: 1067, height: 600 }
+ * ```
+ */
+export const expandToAspectRatio = (
+  dimensions: Dimensions,
+  aspectRatio: Dimensions,
+): Dimensions => {
+  const originalWidth = dimensions.width;
+  const originalHeight = dimensions.height;
+
+  const originalAspectRatio = originalWidth / originalHeight;
+  const targetAspectRatio = aspectRatio.width / aspectRatio.height;
+
+  let newWidth = Math.round(originalWidth);
+  let newHeight = Math.round(originalHeight);
+
+  // Expand by width
+  if (originalAspectRatio > targetAspectRatio) {
+    newWidth = Math.round(originalHeight * targetAspectRatio);
+  }
+  // Expand by height
+  else {
+    newHeight = Math.round(originalWidth / targetAspectRatio);
+  }
+
+  return {
+    width: newWidth,
+    height: newHeight,
+  };
+};