feat: resize canvas to aspect ratio when exporting with fancy bcg
This commit is contained in:
parent
4eabb8d021
commit
c49bf04801
@ -186,7 +186,11 @@ const ImageExportModal = ({
|
|||||||
appState,
|
appState,
|
||||||
files,
|
files,
|
||||||
exportPadding: DEFAULT_EXPORT_PADDING,
|
exportPadding: DEFAULT_EXPORT_PADDING,
|
||||||
maxWidthOrHeight,
|
maxWidthOrHeight: !(
|
||||||
|
exportBackgroundImage !== "solid" && exportWithBackground
|
||||||
|
)
|
||||||
|
? maxWidthOrHeight
|
||||||
|
: undefined,
|
||||||
})
|
})
|
||||||
.then((canvas) => {
|
.then((canvas) => {
|
||||||
setRenderError(null);
|
setRenderError(null);
|
||||||
|
@ -2,8 +2,12 @@ import rough from "roughjs/bin/rough";
|
|||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
|
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
|
||||||
import { renderSceneToSvg, renderStaticScene } from "../renderer/renderScene";
|
import { renderSceneToSvg, renderStaticScene } from "../renderer/renderScene";
|
||||||
import { distance, isOnlyExportingSingleFrame } from "../utils";
|
import {
|
||||||
import { AppState, BinaryFiles } from "../types";
|
distance,
|
||||||
|
expandToAspectRatio,
|
||||||
|
isOnlyExportingSingleFrame,
|
||||||
|
} from "../utils";
|
||||||
|
import { AppState, BinaryFiles, Dimensions } from "../types";
|
||||||
import {
|
import {
|
||||||
DEFAULT_EXPORT_PADDING,
|
DEFAULT_EXPORT_PADDING,
|
||||||
FANCY_BACKGROUND_IMAGES,
|
FANCY_BACKGROUND_IMAGES,
|
||||||
@ -58,7 +62,11 @@ export const exportToCanvas = async (
|
|||||||
? exportPadding
|
? exportPadding
|
||||||
: getFancyBackgroundPadding(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);
|
const { canvas, scale = 1 } = createCanvas(width, height);
|
||||||
|
|
||||||
@ -285,6 +293,7 @@ export const exportToSvg = async (
|
|||||||
const getCanvasSize = (
|
const getCanvasSize = (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
exportPadding: number,
|
exportPadding: number,
|
||||||
|
opts?: { aspectRatio: Dimensions },
|
||||||
): [number, number, number, number] => {
|
): [number, number, number, number] => {
|
||||||
// we should decide if we are exporting the whole canvas
|
// we should decide if we are exporting the whole canvas
|
||||||
// if so, we are not clipping elements in the frame
|
// 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 [minX, minY, maxX, maxY] = getCommonBounds(elements);
|
||||||
const width =
|
const width = distance(minX, maxX) + padding;
|
||||||
distance(minX, maxX) + (onlyExportingSingleFrame ? 0 : exportPadding * 2);
|
const height = distance(minY, maxY) + padding;
|
||||||
const height =
|
|
||||||
distance(minY, maxY) + (onlyExportingSingleFrame ? 0 : exportPadding * 2);
|
if (opts?.aspectRatio) {
|
||||||
|
const expandedDimensions = expandToAspectRatio(
|
||||||
|
{ width, height },
|
||||||
|
opts.aspectRatio,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [minX, minY, expandedDimensions.width, expandedDimensions.height];
|
||||||
|
}
|
||||||
|
|
||||||
return [minX, minY, width, height];
|
return [minX, minY, width, height];
|
||||||
};
|
};
|
||||||
|
47
src/utils.ts
47
src/utils.ts
@ -16,7 +16,7 @@ import {
|
|||||||
FontString,
|
FontString,
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
} from "./element/types";
|
} 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 { unstable_batchedUpdates } from "react-dom";
|
||||||
import { SHAPES } from "./shapes";
|
import { SHAPES } from "./shapes";
|
||||||
import { isEraserActive, isHandToolActive } from "./appState";
|
import { isEraserActive, isHandToolActive } from "./appState";
|
||||||
@ -1007,3 +1007,48 @@ export const isRenderThrottlingEnabled = (() => {
|
|||||||
export const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
|
export const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
|
||||||
? devicePixelRatio
|
? devicePixelRatio
|
||||||
: 1;
|
: 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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user