2
0
Эх сурвалжийг харах

feat: support toggling export padding

dwelle 2 жил өмнө
parent
commit
0314e81396

+ 22 - 0
src/actions/actionExport.tsx

@@ -89,6 +89,28 @@ export const actionChangeExportScale = register({
   },
   },
 });
 });
 
 
+export const actionChangeExportPadding = register({
+  name: "changeExportPadding",
+  trackEvent: { category: "export", action: "togglePadding" },
+  perform: (_elements, appState, value) => {
+    return {
+      appState: {
+        ...appState,
+        exportPadding: value ? DEFAULT_EXPORT_PADDING : 0,
+      },
+      commitToHistory: false,
+    };
+  },
+  PanelComponent: ({ appState, updateData }) => (
+    <CheckboxItem
+      checked={!!appState.exportPadding}
+      onChange={(checked) => updateData(checked)}
+    >
+      {"Padding"}
+    </CheckboxItem>
+  ),
+});
+
 export const actionChangeExportBackground = register({
 export const actionChangeExportBackground = register({
   name: "changeExportBackground",
   name: "changeExportBackground",
   trackEvent: { category: "export", action: "toggleBackground" },
   trackEvent: { category: "export", action: "toggleBackground" },

+ 1 - 0
src/actions/types.ts

@@ -68,6 +68,7 @@ export type ActionName =
   | "finalize"
   | "finalize"
   | "changeProjectName"
   | "changeProjectName"
   | "changeExportBackground"
   | "changeExportBackground"
+  | "changeExportPadding"
   | "changeExportEmbedScene"
   | "changeExportEmbedScene"
   | "changeExportScale"
   | "changeExportScale"
   | "saveToActiveFile"
   | "saveToActiveFile"

+ 3 - 0
src/appState.ts

@@ -1,5 +1,6 @@
 import oc from "open-color";
 import oc from "open-color";
 import {
 import {
+  DEFAULT_EXPORT_PADDING,
   DEFAULT_FONT_FAMILY,
   DEFAULT_FONT_FAMILY,
   DEFAULT_FONT_SIZE,
   DEFAULT_FONT_SIZE,
   DEFAULT_TEXT_ALIGN,
   DEFAULT_TEXT_ALIGN,
@@ -55,6 +56,7 @@ export const getDefaultAppState = (): Omit<
     exportScale: defaultExportScale,
     exportScale: defaultExportScale,
     exportEmbedScene: false,
     exportEmbedScene: false,
     exportWithDarkMode: false,
     exportWithDarkMode: false,
+    exportPadding: DEFAULT_EXPORT_PADDING,
     fileHandle: null,
     fileHandle: null,
     gridSize: null,
     gridSize: null,
     isBindingEnabled: true,
     isBindingEnabled: true,
@@ -145,6 +147,7 @@ const APP_STATE_STORAGE_CONF = (<
   exportBackground: { browser: true, export: false, server: false },
   exportBackground: { browser: true, export: false, server: false },
   exportEmbedScene: { browser: true, export: false, server: false },
   exportEmbedScene: { browser: true, export: false, server: false },
   exportScale: { browser: true, export: false, server: false },
   exportScale: { browser: true, export: false, server: false },
+  exportPadding: { browser: true, export: false, server: false },
   exportWithDarkMode: { browser: true, export: false, server: false },
   exportWithDarkMode: { browser: true, export: false, server: false },
   fileHandle: { browser: false, export: false, server: false },
   fileHandle: { browser: false, export: false, server: false },
   gridSize: { browser: true, export: true, server: true },
   gridSize: { browser: true, export: true, server: true },

+ 3 - 7
src/components/ImageExportDialog.tsx

@@ -79,7 +79,6 @@ const ImageExportModal = ({
   elements,
   elements,
   appState,
   appState,
   files,
   files,
-  exportPadding = DEFAULT_EXPORT_PADDING,
   actionManager,
   actionManager,
   onExportToPng,
   onExportToPng,
   onExportToSvg,
   onExportToSvg,
@@ -88,7 +87,6 @@ const ImageExportModal = ({
   appState: AppState;
   appState: AppState;
   elements: readonly NonDeletedExcalidrawElement[];
   elements: readonly NonDeletedExcalidrawElement[];
   files: BinaryFiles;
   files: BinaryFiles;
-  exportPadding?: number;
   actionManager: ActionManager;
   actionManager: ActionManager;
   onExportToPng: ExportCB;
   onExportToPng: ExportCB;
   onExportToSvg: ExportCB;
   onExportToSvg: ExportCB;
@@ -116,7 +114,7 @@ const ImageExportModal = ({
     exportToCanvas(exportedElements, appState, files, {
     exportToCanvas(exportedElements, appState, files, {
       exportBackground,
       exportBackground,
       viewBackgroundColor,
       viewBackgroundColor,
-      exportPadding,
+      exportPadding: appState.exportPadding,
     })
     })
       .then((canvas) => {
       .then((canvas) => {
         // if converting to blob fails, there's some problem that will
         // if converting to blob fails, there's some problem that will
@@ -134,7 +132,6 @@ const ImageExportModal = ({
     files,
     files,
     exportedElements,
     exportedElements,
     exportBackground,
     exportBackground,
-    exportPadding,
     viewBackgroundColor,
     viewBackgroundColor,
   ]);
   ]);
 
 
@@ -151,8 +148,10 @@ const ImageExportModal = ({
             // dunno why this is needed, but when the items wrap it creates
             // dunno why this is needed, but when the items wrap it creates
             // an overflow
             // an overflow
             overflow: "hidden",
             overflow: "hidden",
+            gap: ".6rem",
           }}
           }}
         >
         >
+          {actionManager.renderAction("changeExportPadding")}
           {actionManager.renderAction("changeExportBackground")}
           {actionManager.renderAction("changeExportBackground")}
           {someElementIsSelected && (
           {someElementIsSelected && (
             <CheckboxItem
             <CheckboxItem
@@ -221,7 +220,6 @@ export const ImageExportDialog = ({
   appState,
   appState,
   setAppState,
   setAppState,
   files,
   files,
-  exportPadding = DEFAULT_EXPORT_PADDING,
   actionManager,
   actionManager,
   onExportToPng,
   onExportToPng,
   onExportToSvg,
   onExportToSvg,
@@ -231,7 +229,6 @@ export const ImageExportDialog = ({
   setAppState: React.Component<any, AppState>["setState"];
   setAppState: React.Component<any, AppState>["setState"];
   elements: readonly NonDeletedExcalidrawElement[];
   elements: readonly NonDeletedExcalidrawElement[];
   files: BinaryFiles;
   files: BinaryFiles;
-  exportPadding?: number;
   actionManager: ActionManager;
   actionManager: ActionManager;
   onExportToPng: ExportCB;
   onExportToPng: ExportCB;
   onExportToSvg: ExportCB;
   onExportToSvg: ExportCB;
@@ -249,7 +246,6 @@ export const ImageExportDialog = ({
             elements={elements}
             elements={elements}
             appState={appState}
             appState={appState}
             files={files}
             files={files}
-            exportPadding={exportPadding}
             actionManager={actionManager}
             actionManager={actionManager}
             onExportToPng={onExportToPng}
             onExportToPng={onExportToPng}
             onExportToSvg={onExportToSvg}
             onExportToSvg={onExportToSvg}

+ 1 - 0
src/components/LayerUI.tsx

@@ -144,6 +144,7 @@ const LayerUI = ({
             exportBackground: appState.exportBackground,
             exportBackground: appState.exportBackground,
             name: appState.name,
             name: appState.name,
             viewBackgroundColor: appState.viewBackgroundColor,
             viewBackgroundColor: appState.viewBackgroundColor,
+            exportPadding: appState.exportPadding,
           },
           },
         )
         )
           .catch(muteFSAbortError)
           .catch(muteFSAbortError)

+ 1 - 1
src/element/bounds.ts

@@ -492,7 +492,7 @@ export const getElementBounds = (
 
 
 export const getCommonBounds = (
 export const getCommonBounds = (
   elements: readonly ExcalidrawElement[],
   elements: readonly ExcalidrawElement[],
-): [number, number, number, number] => {
+): [minX: number, minY: number, maxX: number, maxY: number] => {
   if (!elements.length) {
   if (!elements.length) {
     return [0, 0, 0, 0];
     return [0, 0, 0, 0];
   }
   }

+ 9 - 2
src/scene/export.ts

@@ -177,9 +177,16 @@ const getCanvasSize = (
   elements: readonly NonDeletedExcalidrawElement[],
   elements: readonly NonDeletedExcalidrawElement[],
   exportPadding: number,
   exportPadding: number,
 ): [number, number, number, number] => {
 ): [number, number, number, number] => {
-  const [minX, minY, maxX, maxY] = getCommonBounds(elements);
+  const bounds = getCommonBounds(elements);
+
+  const minX = Math.floor(bounds[0]);
+  const minY = Math.floor(bounds[1]);
+  const maxX = Math.ceil(bounds[2]);
+  const maxY = Math.ceil(bounds[3]);
+
   const width = distance(minX, maxX) + exportPadding * 2;
   const width = distance(minX, maxX) + exportPadding * 2;
-  const height = distance(minY, maxY) + exportPadding + exportPadding;
+  const height =
+    Math.ceil(distance(minY, maxY)) + exportPadding + exportPadding;
 
 
   return [minX, minY, width, height];
   return [minX, minY, width, height];
 };
 };

+ 1 - 0
src/types.ts

@@ -113,6 +113,7 @@ export type AppState = {
   exportEmbedScene: boolean;
   exportEmbedScene: boolean;
   exportWithDarkMode: boolean;
   exportWithDarkMode: boolean;
   exportScale: number;
   exportScale: number;
+  exportPadding: number;
   currentItemStrokeColor: string;
   currentItemStrokeColor: string;
   currentItemBackgroundColor: string;
   currentItemBackgroundColor: string;
   currentItemFillStyle: ExcalidrawElement["fillStyle"];
   currentItemFillStyle: ExcalidrawElement["fillStyle"];