From 4c8cf9c91c96413b9af6e2887054fa4ad8473732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arno=C5=A1t=20Pleskot?= Date: Tue, 15 Aug 2023 23:41:52 +0200 Subject: [PATCH] feat: scale up small exports with fancy background --- src/appState.ts | 7 +--- src/components/ImageExportDialog.tsx | 52 +++++++++++++++++++++++++--- src/scene/export.ts | 7 ++-- src/scene/fancyBackground.ts | 5 +++ src/types.ts | 2 ++ src/utils.ts | 5 +++ 6 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/appState.ts b/src/appState.ts index 606d1f97b..6a00ccfd6 100644 --- a/src/appState.ts +++ b/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, diff --git a/src/components/ImageExportDialog.tsx b/src/components/ImageExportDialog.tsx index 3f0587911..d0e4a75eb 100644 --- a/src/components/ImageExportDialog.tsx +++ b/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(null); const [renderError, setRenderError] = useState(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`, }))} /> diff --git a/src/scene/export.ts b/src/scene/export.ts index 604899c80..4ccd4faba 100644 --- a/src/scene/export.ts +++ b/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 = ``; @@ -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) { diff --git a/src/scene/fancyBackground.ts b/src/scene/fancyBackground.ts index 4c412012b..c68ef2c40 100644 --- a/src/scene/fancyBackground.ts +++ b/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, diff --git a/src/types.ts b/src/types.ts index 5b823fb91..d297eb03d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -657,3 +657,5 @@ export type FrameNameBoundsCache = { } >; }; + +export type Dimensions = { width: number; height: number }; diff --git a/src/utils.ts b/src/utils.ts index 8b142744a..226725f49 100644 --- a/src/utils.ts +++ b/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;