Bladeren bron

feat: scale up small exports with fancy background

Arnošt Pleskot 2 jaren geleden
bovenliggende
commit
4c8cf9c91c
6 gewijzigde bestanden met toevoegingen van 64 en 14 verwijderingen
  1. 1 6
      src/appState.ts
  2. 48 4
      src/components/ImageExportDialog.tsx
  3. 3 4
      src/scene/export.ts
  4. 5 0
      src/scene/fancyBackground.ts
  5. 2 0
      src/types.ts
  6. 5 0
      src/utils.ts

+ 1 - 6
src/appState.ts

@@ -5,16 +5,11 @@ import {
   DEFAULT_FONT_FAMILY,
   DEFAULT_FONT_SIZE,
   DEFAULT_TEXT_ALIGN,
-  EXPORT_SCALES,
   THEME,
 } from "./constants";
 import { t } from "./i18n";
 import { AppState, NormalizedZoomValue } from "./types";
-import { getDateTime } from "./utils";
-
-const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
-  ? devicePixelRatio
-  : 1;
+import { getDateTime, defaultExportScale } from "./utils";
 
 export const getDefaultAppState = (): Omit<
   AppState,

+ 48 - 4
src/components/ImageExportDialog.tsx

@@ -26,7 +26,7 @@ import { nativeFileSystemSupported } from "../data/filesystem";
 import { NonDeletedExcalidrawElement } from "../element/types";
 import { t } from "../i18n";
 import { getSelectedElements, isSomeElementSelected } from "../scene";
-import { exportToCanvas } from "../packages/utils";
+import { exportToCanvas, getScaleToFit } from "../packages/utils";
 
 import { copyIcon, downloadIcon, helpIcon } from "./icons";
 import { Dialog } from "./Dialog";
@@ -38,6 +38,9 @@ import "./ImageExportDialog.scss";
 import { useAppProps } from "./App";
 import { FilledButton } from "./FilledButton";
 import Select, { convertToSelectItems } from "./Select";
+import { getCommonBounds } from "../element";
+import { defaultExportScale, distance } from "../utils";
+import { getFancyBackgroundPadding } from "../scene/fancyBackground";
 
 const supportsContextFilters =
   "filter" in document.createElement("canvas").getContext("2d")!;
@@ -98,6 +101,7 @@ const ImageExportModal = ({
   );
   const [embedScene, setEmbedScene] = useState(appState.exportEmbedScene);
   const [exportScale, setExportScale] = useState(appState.exportScale);
+  const [exportBaseScale, setExportBaseScale] = useState(appState.exportScale);
 
   const previewRef = useRef<HTMLDivElement>(null);
   const [renderError, setRenderError] = useState<Error | null>(null);
@@ -109,6 +113,44 @@ const ImageExportModal = ({
       })
     : elements;
 
+  useEffect(() => {
+    if (
+      exportedElements.length > 0 &&
+      exportWithBackground &&
+      exportBackgroundImage !== "solid"
+    ) {
+      const previewNode = previewRef.current;
+      if (!previewNode) {
+        return;
+      }
+      const [minX, minY, maxX, maxY] = getCommonBounds(exportedElements);
+      const maxWidth = previewNode.offsetWidth;
+      const maxHeight = previewNode.offsetHeight;
+
+      const scale =
+        Math.floor(
+          (getScaleToFit(
+            {
+              width: distance(minX, maxX) + getFancyBackgroundPadding() * 2,
+              height: distance(minY, maxY) + getFancyBackgroundPadding() * 2,
+            },
+            { width: maxWidth, height: maxHeight },
+          ) +
+            Number.EPSILON) *
+            100,
+        ) / 100;
+
+      if (scale > 1) {
+        actionManager.executeAction(actionChangeExportScale, "ui", scale);
+        setExportBaseScale(scale);
+      } else {
+        setExportBaseScale(defaultExportScale);
+      }
+    } else {
+      setExportBaseScale(defaultExportScale);
+    }
+  }, [actionManager, exportedElements, previewRef]);
+
   useEffect(() => {
     const previewNode = previewRef.current;
     if (!previewNode) {
@@ -117,6 +159,8 @@ const ImageExportModal = ({
     const maxWidth = previewNode.offsetWidth;
     const maxHeight = previewNode.offsetHeight;
 
+    const maxWidthOrHeight = Math.min(maxWidth, maxHeight);
+
     if (!maxWidth) {
       return;
     }
@@ -125,7 +169,7 @@ const ImageExportModal = ({
       appState,
       files,
       exportPadding: DEFAULT_EXPORT_PADDING,
-      maxWidthOrHeight: Math.max(maxWidth, maxHeight),
+      maxWidthOrHeight,
     })
       .then((canvas) => {
         setRenderError(null);
@@ -283,8 +327,8 @@ const ImageExportModal = ({
               actionManager.executeAction(actionChangeExportScale, "ui", scale);
             }}
             choices={EXPORT_SCALES.map((scale) => ({
-              value: scale,
-              label: `${scale}\u00d7`,
+              value: scale * exportBaseScale,
+              label: `${scale * exportBaseScale}\u00d7`,
             }))}
           />
         </ExportSetting>

+ 3 - 4
src/scene/export.ts

@@ -7,8 +7,6 @@ import { AppState, BinaryFiles } from "../types";
 import {
   DEFAULT_EXPORT_PADDING,
   FANCY_BACKGROUND_IMAGES,
-  FANCY_BG_BORDER_RADIUS,
-  FANCY_BG_PADDING,
   SVG_NS,
   THEME,
   THEME_FILTER,
@@ -23,6 +21,7 @@ import Scene from "./Scene";
 import {
   applyFancyBackgroundOnCanvas,
   applyFancyBackgroundOnSvg,
+  getFancyBackgroundPadding,
 } from "./fancyBackground";
 
 export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
@@ -57,7 +56,7 @@ export const exportToCanvas = async (
     elements.length > 0;
   const padding = !exportWithFancyBackground
     ? exportPadding
-    : exportPadding + FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS;
+    : getFancyBackgroundPadding(exportPadding);
 
   const [minX, minY, width, height] = getCanvasSize(elements, padding);
 
@@ -169,7 +168,7 @@ export const exportToSvg = async (
 
   const padding = !exportWithFancyBackground
     ? exportPadding
-    : (exportPadding + FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS) * exportScale;
+    : getFancyBackgroundPadding(exportPadding) * exportScale;
 
   let metadata = "";
   if (exportEmbedScene) {

+ 5 - 0
src/scene/fancyBackground.ts

@@ -1,4 +1,5 @@
 import {
+  DEFAULT_EXPORT_PADDING,
   FANCY_BACKGROUND_IMAGES,
   FANCY_BG_BORDER_RADIUS,
   FANCY_BG_PADDING,
@@ -12,6 +13,10 @@ import { getScaleToFill } from "../packages/utils";
 import { roundRect } from "../renderer/roundRect";
 import { AppState, Dimensions } from "../types";
 
+export const getFancyBackgroundPadding = (
+  exportPadding = DEFAULT_EXPORT_PADDING,
+) => FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS + exportPadding;
+
 const addImageBackground = (
   context: CanvasRenderingContext2D,
   canvasDimensions: Dimensions,

+ 2 - 0
src/types.ts

@@ -657,3 +657,5 @@ export type FrameNameBoundsCache = {
     }
   >;
 };
+
+export type Dimensions = { width: number; height: number };

+ 5 - 0
src/utils.ts

@@ -4,6 +4,7 @@ import {
   CURSOR_TYPE,
   DEFAULT_VERSION,
   EVENT,
+  EXPORT_SCALES,
   FONT_FAMILY,
   isDarwin,
   MIME_TYPES,
@@ -1002,3 +1003,7 @@ export const isRenderThrottlingEnabled = (() => {
     return false;
   };
 })();
+
+export const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
+  ? devicePixelRatio
+  : 1;