feat: scale up small exports with fancy background
This commit is contained in:
parent
d27856a967
commit
4c8cf9c91c
@ -5,16 +5,11 @@ import {
|
|||||||
DEFAULT_FONT_FAMILY,
|
DEFAULT_FONT_FAMILY,
|
||||||
DEFAULT_FONT_SIZE,
|
DEFAULT_FONT_SIZE,
|
||||||
DEFAULT_TEXT_ALIGN,
|
DEFAULT_TEXT_ALIGN,
|
||||||
EXPORT_SCALES,
|
|
||||||
THEME,
|
THEME,
|
||||||
} from "./constants";
|
} from "./constants";
|
||||||
import { t } from "./i18n";
|
import { t } from "./i18n";
|
||||||
import { AppState, NormalizedZoomValue } from "./types";
|
import { AppState, NormalizedZoomValue } from "./types";
|
||||||
import { getDateTime } from "./utils";
|
import { getDateTime, defaultExportScale } from "./utils";
|
||||||
|
|
||||||
const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
|
|
||||||
? devicePixelRatio
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
export const getDefaultAppState = (): Omit<
|
export const getDefaultAppState = (): Omit<
|
||||||
AppState,
|
AppState,
|
||||||
|
@ -26,7 +26,7 @@ import { nativeFileSystemSupported } from "../data/filesystem";
|
|||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
||||||
import { exportToCanvas } from "../packages/utils";
|
import { exportToCanvas, getScaleToFit } from "../packages/utils";
|
||||||
|
|
||||||
import { copyIcon, downloadIcon, helpIcon } from "./icons";
|
import { copyIcon, downloadIcon, helpIcon } from "./icons";
|
||||||
import { Dialog } from "./Dialog";
|
import { Dialog } from "./Dialog";
|
||||||
@ -38,6 +38,9 @@ import "./ImageExportDialog.scss";
|
|||||||
import { useAppProps } from "./App";
|
import { useAppProps } from "./App";
|
||||||
import { FilledButton } from "./FilledButton";
|
import { FilledButton } from "./FilledButton";
|
||||||
import Select, { convertToSelectItems } from "./Select";
|
import Select, { convertToSelectItems } from "./Select";
|
||||||
|
import { getCommonBounds } from "../element";
|
||||||
|
import { defaultExportScale, distance } from "../utils";
|
||||||
|
import { getFancyBackgroundPadding } from "../scene/fancyBackground";
|
||||||
|
|
||||||
const supportsContextFilters =
|
const supportsContextFilters =
|
||||||
"filter" in document.createElement("canvas").getContext("2d")!;
|
"filter" in document.createElement("canvas").getContext("2d")!;
|
||||||
@ -98,6 +101,7 @@ const ImageExportModal = ({
|
|||||||
);
|
);
|
||||||
const [embedScene, setEmbedScene] = useState(appState.exportEmbedScene);
|
const [embedScene, setEmbedScene] = useState(appState.exportEmbedScene);
|
||||||
const [exportScale, setExportScale] = useState(appState.exportScale);
|
const [exportScale, setExportScale] = useState(appState.exportScale);
|
||||||
|
const [exportBaseScale, setExportBaseScale] = useState(appState.exportScale);
|
||||||
|
|
||||||
const previewRef = useRef<HTMLDivElement>(null);
|
const previewRef = useRef<HTMLDivElement>(null);
|
||||||
const [renderError, setRenderError] = useState<Error | null>(null);
|
const [renderError, setRenderError] = useState<Error | null>(null);
|
||||||
@ -109,6 +113,44 @@ const ImageExportModal = ({
|
|||||||
})
|
})
|
||||||
: elements;
|
: 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(() => {
|
useEffect(() => {
|
||||||
const previewNode = previewRef.current;
|
const previewNode = previewRef.current;
|
||||||
if (!previewNode) {
|
if (!previewNode) {
|
||||||
@ -117,6 +159,8 @@ const ImageExportModal = ({
|
|||||||
const maxWidth = previewNode.offsetWidth;
|
const maxWidth = previewNode.offsetWidth;
|
||||||
const maxHeight = previewNode.offsetHeight;
|
const maxHeight = previewNode.offsetHeight;
|
||||||
|
|
||||||
|
const maxWidthOrHeight = Math.min(maxWidth, maxHeight);
|
||||||
|
|
||||||
if (!maxWidth) {
|
if (!maxWidth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -125,7 +169,7 @@ const ImageExportModal = ({
|
|||||||
appState,
|
appState,
|
||||||
files,
|
files,
|
||||||
exportPadding: DEFAULT_EXPORT_PADDING,
|
exportPadding: DEFAULT_EXPORT_PADDING,
|
||||||
maxWidthOrHeight: Math.max(maxWidth, maxHeight),
|
maxWidthOrHeight,
|
||||||
})
|
})
|
||||||
.then((canvas) => {
|
.then((canvas) => {
|
||||||
setRenderError(null);
|
setRenderError(null);
|
||||||
@ -283,8 +327,8 @@ const ImageExportModal = ({
|
|||||||
actionManager.executeAction(actionChangeExportScale, "ui", scale);
|
actionManager.executeAction(actionChangeExportScale, "ui", scale);
|
||||||
}}
|
}}
|
||||||
choices={EXPORT_SCALES.map((scale) => ({
|
choices={EXPORT_SCALES.map((scale) => ({
|
||||||
value: scale,
|
value: scale * exportBaseScale,
|
||||||
label: `${scale}\u00d7`,
|
label: `${scale * exportBaseScale}\u00d7`,
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</ExportSetting>
|
</ExportSetting>
|
||||||
|
@ -7,8 +7,6 @@ import { AppState, BinaryFiles } from "../types";
|
|||||||
import {
|
import {
|
||||||
DEFAULT_EXPORT_PADDING,
|
DEFAULT_EXPORT_PADDING,
|
||||||
FANCY_BACKGROUND_IMAGES,
|
FANCY_BACKGROUND_IMAGES,
|
||||||
FANCY_BG_BORDER_RADIUS,
|
|
||||||
FANCY_BG_PADDING,
|
|
||||||
SVG_NS,
|
SVG_NS,
|
||||||
THEME,
|
THEME,
|
||||||
THEME_FILTER,
|
THEME_FILTER,
|
||||||
@ -23,6 +21,7 @@ import Scene from "./Scene";
|
|||||||
import {
|
import {
|
||||||
applyFancyBackgroundOnCanvas,
|
applyFancyBackgroundOnCanvas,
|
||||||
applyFancyBackgroundOnSvg,
|
applyFancyBackgroundOnSvg,
|
||||||
|
getFancyBackgroundPadding,
|
||||||
} from "./fancyBackground";
|
} from "./fancyBackground";
|
||||||
|
|
||||||
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
||||||
@ -57,7 +56,7 @@ export const exportToCanvas = async (
|
|||||||
elements.length > 0;
|
elements.length > 0;
|
||||||
const padding = !exportWithFancyBackground
|
const padding = !exportWithFancyBackground
|
||||||
? exportPadding
|
? exportPadding
|
||||||
: exportPadding + FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS;
|
: getFancyBackgroundPadding(exportPadding);
|
||||||
|
|
||||||
const [minX, minY, width, height] = getCanvasSize(elements, padding);
|
const [minX, minY, width, height] = getCanvasSize(elements, padding);
|
||||||
|
|
||||||
@ -169,7 +168,7 @@ export const exportToSvg = async (
|
|||||||
|
|
||||||
const padding = !exportWithFancyBackground
|
const padding = !exportWithFancyBackground
|
||||||
? exportPadding
|
? exportPadding
|
||||||
: (exportPadding + FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS) * exportScale;
|
: getFancyBackgroundPadding(exportPadding) * exportScale;
|
||||||
|
|
||||||
let metadata = "";
|
let metadata = "";
|
||||||
if (exportEmbedScene) {
|
if (exportEmbedScene) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
DEFAULT_EXPORT_PADDING,
|
||||||
FANCY_BACKGROUND_IMAGES,
|
FANCY_BACKGROUND_IMAGES,
|
||||||
FANCY_BG_BORDER_RADIUS,
|
FANCY_BG_BORDER_RADIUS,
|
||||||
FANCY_BG_PADDING,
|
FANCY_BG_PADDING,
|
||||||
@ -12,6 +13,10 @@ import { getScaleToFill } from "../packages/utils";
|
|||||||
import { roundRect } from "../renderer/roundRect";
|
import { roundRect } from "../renderer/roundRect";
|
||||||
import { AppState, Dimensions } from "../types";
|
import { AppState, Dimensions } from "../types";
|
||||||
|
|
||||||
|
export const getFancyBackgroundPadding = (
|
||||||
|
exportPadding = DEFAULT_EXPORT_PADDING,
|
||||||
|
) => FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS + exportPadding;
|
||||||
|
|
||||||
const addImageBackground = (
|
const addImageBackground = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
canvasDimensions: Dimensions,
|
canvasDimensions: Dimensions,
|
||||||
|
@ -657,3 +657,5 @@ export type FrameNameBoundsCache = {
|
|||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Dimensions = { width: number; height: number };
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
CURSOR_TYPE,
|
CURSOR_TYPE,
|
||||||
DEFAULT_VERSION,
|
DEFAULT_VERSION,
|
||||||
EVENT,
|
EVENT,
|
||||||
|
EXPORT_SCALES,
|
||||||
FONT_FAMILY,
|
FONT_FAMILY,
|
||||||
isDarwin,
|
isDarwin,
|
||||||
MIME_TYPES,
|
MIME_TYPES,
|
||||||
@ -1002,3 +1003,7 @@ export const isRenderThrottlingEnabled = (() => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
export const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
|
||||||
|
? devicePixelRatio
|
||||||
|
: 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user