feat: resize canvas to aspect ratio when exporting with fancy bcg

This commit is contained in:
Arnošt Pleskot 2023-08-22 17:59:25 +02:00
parent 4eabb8d021
commit c49bf04801
No known key found for this signature in database
3 changed files with 76 additions and 9 deletions

View File

@ -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);

View File

@ -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];
}; };

View File

@ -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,
};
};