mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: introduce frames (#6123)
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
4d7d96eb7b
commit
81ebf82979
78 changed files with 4563 additions and 480 deletions
|
@ -1,8 +1,8 @@
|
|||
import rough from "roughjs/bin/rough";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { getCommonBounds } from "../element/bounds";
|
||||
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
|
||||
import { renderScene, renderSceneToSvg } from "../renderer/renderScene";
|
||||
import { distance } from "../utils";
|
||||
import { distance, isOnlyExportingSingleFrame } from "../utils";
|
||||
import { AppState, BinaryFiles } from "../types";
|
||||
import { DEFAULT_EXPORT_PADDING, SVG_NS, THEME_FILTER } from "../constants";
|
||||
import { getDefaultAppState } from "../appState";
|
||||
|
@ -11,6 +11,7 @@ import {
|
|||
getInitializedImageElements,
|
||||
updateImageCache,
|
||||
} from "../element/image";
|
||||
import Scene from "./Scene";
|
||||
|
||||
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
||||
|
||||
|
@ -51,6 +52,8 @@ export const exportToCanvas = async (
|
|||
files,
|
||||
});
|
||||
|
||||
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
||||
|
||||
renderScene({
|
||||
elements,
|
||||
appState,
|
||||
|
@ -59,8 +62,8 @@ export const exportToCanvas = async (
|
|||
canvas,
|
||||
renderConfig: {
|
||||
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
|
||||
scrollX: -minX + exportPadding,
|
||||
scrollY: -minY + exportPadding,
|
||||
scrollX: -minX + (onlyExportingSingleFrame ? 0 : exportPadding),
|
||||
scrollY: -minY + (onlyExportingSingleFrame ? 0 : exportPadding),
|
||||
zoom: defaultAppState.zoom,
|
||||
remotePointerViewportCoords: {},
|
||||
remoteSelectedElementIds: {},
|
||||
|
@ -88,6 +91,7 @@ export const exportToSvg = async (
|
|||
viewBackgroundColor: string;
|
||||
exportWithDarkMode?: boolean;
|
||||
exportEmbedScene?: boolean;
|
||||
renderFrame?: boolean;
|
||||
},
|
||||
files: BinaryFiles | null,
|
||||
opts?: {
|
||||
|
@ -140,6 +144,39 @@ export const exportToSvg = async (
|
|||
}
|
||||
assetPath = `${assetPath}/dist/excalidraw-assets/`;
|
||||
}
|
||||
|
||||
// do not apply clipping when we're exporting the whole scene
|
||||
const isExportingWholeCanvas =
|
||||
Scene.getScene(elements[0])?.getNonDeletedElements()?.length ===
|
||||
elements.length;
|
||||
|
||||
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
||||
|
||||
const offsetX = -minX + (onlyExportingSingleFrame ? 0 : exportPadding);
|
||||
const offsetY = -minY + (onlyExportingSingleFrame ? 0 : exportPadding);
|
||||
|
||||
const exportingFrame =
|
||||
isExportingWholeCanvas || !onlyExportingSingleFrame
|
||||
? undefined
|
||||
: elements.find((element) => element.type === "frame");
|
||||
|
||||
let exportingFrameClipPath = "";
|
||||
if (exportingFrame) {
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(exportingFrame);
|
||||
const cx = (x2 - x1) / 2 - (exportingFrame.x - x1);
|
||||
const cy = (y2 - y1) / 2 - (exportingFrame.y - y1);
|
||||
|
||||
exportingFrameClipPath = `<clipPath id=${exportingFrame.id}>
|
||||
<rect transform="translate(${exportingFrame.x + offsetX} ${
|
||||
exportingFrame.y + offsetY
|
||||
}) rotate(${exportingFrame.angle} ${cx} ${cy})"
|
||||
width="${exportingFrame.width}"
|
||||
height="${exportingFrame.height}"
|
||||
>
|
||||
</rect>
|
||||
</clipPath>`;
|
||||
}
|
||||
|
||||
svgRoot.innerHTML = `
|
||||
${SVG_EXPORT_TAG}
|
||||
${metadata}
|
||||
|
@ -154,8 +191,10 @@ export const exportToSvg = async (
|
|||
src: url("${assetPath}Cascadia.woff2");
|
||||
}
|
||||
</style>
|
||||
${exportingFrameClipPath}
|
||||
</defs>
|
||||
`;
|
||||
|
||||
// render background rect
|
||||
if (appState.exportBackground && viewBackgroundColor) {
|
||||
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
|
||||
|
@ -169,9 +208,10 @@ export const exportToSvg = async (
|
|||
|
||||
const rsvg = rough.svg(svgRoot);
|
||||
renderSceneToSvg(elements, rsvg, svgRoot, files || {}, {
|
||||
offsetX: -minX + exportPadding,
|
||||
offsetY: -minY + exportPadding,
|
||||
offsetX,
|
||||
offsetY,
|
||||
exportWithDarkMode: appState.exportWithDarkMode,
|
||||
exportingFrameId: exportingFrame?.id || null,
|
||||
});
|
||||
|
||||
return svgRoot;
|
||||
|
@ -182,9 +222,36 @@ const getCanvasSize = (
|
|||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
exportPadding: number,
|
||||
): [number, number, number, number] => {
|
||||
// we should decide if we are exporting the whole canvas
|
||||
// if so, we are not clipping elements in the frame
|
||||
// and therefore, we should not do anything special
|
||||
|
||||
const isExportingWholeCanvas =
|
||||
Scene.getScene(elements[0])?.getNonDeletedElements()?.length ===
|
||||
elements.length;
|
||||
|
||||
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
||||
|
||||
if (!isExportingWholeCanvas || onlyExportingSingleFrame) {
|
||||
const frames = elements.filter((element) => element.type === "frame");
|
||||
|
||||
const exportedFrameIds = frames.reduce((acc, frame) => {
|
||||
acc[frame.id] = true;
|
||||
return acc;
|
||||
}, {} as Record<string, true>);
|
||||
|
||||
// elements in a frame do not affect the canvas size if we're not exporting
|
||||
// the whole canvas
|
||||
elements = elements.filter(
|
||||
(element) => !exportedFrameIds[element.frameId ?? ""],
|
||||
);
|
||||
}
|
||||
|
||||
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
|
||||
const width = distance(minX, maxX) + exportPadding * 2;
|
||||
const height = distance(minY, maxY) + exportPadding + exportPadding;
|
||||
const width =
|
||||
distance(minX, maxX) + (onlyExportingSingleFrame ? 0 : exportPadding * 2);
|
||||
const height =
|
||||
distance(minY, maxY) + (onlyExportingSingleFrame ? 0 : exportPadding * 2);
|
||||
|
||||
return [minX, minY, width, height];
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue