feat: fancyBackgrounds in svg
This commit is contained in:
parent
94f20566d1
commit
e8cc787edc
@ -47,6 +47,7 @@ export const exportCanvas = async (
|
|||||||
exportPadding,
|
exportPadding,
|
||||||
exportScale: appState.exportScale,
|
exportScale: appState.exportScale,
|
||||||
exportEmbedScene: appState.exportEmbedScene && type === "svg",
|
exportEmbedScene: appState.exportEmbedScene && type === "svg",
|
||||||
|
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl,
|
||||||
},
|
},
|
||||||
files,
|
files,
|
||||||
);
|
);
|
||||||
|
@ -123,3 +123,24 @@ export const normalizeSVG = async (SVGString: string) => {
|
|||||||
return svg.outerHTML;
|
return svg.outerHTML;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const loadSVGElement = (filePath: string) => {
|
||||||
|
return new Promise<SVGSVGElement>((resolve, reject) => {
|
||||||
|
fetch(filePath)
|
||||||
|
.then((response) => response.text())
|
||||||
|
.then((svgString) => {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const svgDoc = parser.parseFromString(svgString, "image/svg+xml");
|
||||||
|
const svgElement = svgDoc.documentElement;
|
||||||
|
|
||||||
|
if (svgElement instanceof SVGSVGElement) {
|
||||||
|
resolve(svgElement);
|
||||||
|
} else {
|
||||||
|
reject(new Error("Parsed element is not an SVGSVGElement"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
@ -3,7 +3,7 @@ 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 { distance, isOnlyExportingSingleFrame } from "../utils";
|
||||||
import { AppState, BinaryFiles } from "../types";
|
import { AppState, BinaryFiles, DataURL } from "../types";
|
||||||
import {
|
import {
|
||||||
DEFAULT_EXPORT_PADDING,
|
DEFAULT_EXPORT_PADDING,
|
||||||
FANCY_BG_BORDER_RADIUS,
|
FANCY_BG_BORDER_RADIUS,
|
||||||
@ -19,7 +19,10 @@ import {
|
|||||||
updateImageCache,
|
updateImageCache,
|
||||||
} from "../element/image";
|
} from "../element/image";
|
||||||
import Scene from "./Scene";
|
import Scene from "./Scene";
|
||||||
import { applyFancyBackground } from "./fancyBackground";
|
import {
|
||||||
|
applyFancyBackgroundOnCanvas,
|
||||||
|
applyFancyBackgroundOnSvg,
|
||||||
|
} from "./fancyBackground";
|
||||||
|
|
||||||
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
||||||
|
|
||||||
@ -93,7 +96,7 @@ export const exportToCanvas = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (exportWithFancyBackground) {
|
if (exportWithFancyBackground) {
|
||||||
await applyFancyBackground({
|
await applyFancyBackgroundOnCanvas({
|
||||||
canvas,
|
canvas,
|
||||||
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl!,
|
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl!,
|
||||||
backgroundColor: viewBackgroundColor,
|
backgroundColor: viewBackgroundColor,
|
||||||
@ -136,6 +139,7 @@ export const exportToSvg = async (
|
|||||||
exportWithDarkMode?: boolean;
|
exportWithDarkMode?: boolean;
|
||||||
exportEmbedScene?: boolean;
|
exportEmbedScene?: boolean;
|
||||||
renderFrame?: boolean;
|
renderFrame?: boolean;
|
||||||
|
fancyBackgroundImageUrl: DataURL | null;
|
||||||
},
|
},
|
||||||
files: BinaryFiles | null,
|
files: BinaryFiles | null,
|
||||||
opts?: {
|
opts?: {
|
||||||
@ -148,7 +152,18 @@ export const exportToSvg = async (
|
|||||||
viewBackgroundColor,
|
viewBackgroundColor,
|
||||||
exportScale = 1,
|
exportScale = 1,
|
||||||
exportEmbedScene,
|
exportEmbedScene,
|
||||||
|
exportBackground,
|
||||||
} = appState;
|
} = appState;
|
||||||
|
|
||||||
|
const exportWithFancyBackground =
|
||||||
|
exportBackground &&
|
||||||
|
!!appState.fancyBackgroundImageUrl &&
|
||||||
|
elements.length > 0;
|
||||||
|
|
||||||
|
const padding = !exportWithFancyBackground
|
||||||
|
? exportPadding
|
||||||
|
: (exportPadding + FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS) * exportScale;
|
||||||
|
|
||||||
let metadata = "";
|
let metadata = "";
|
||||||
if (exportEmbedScene) {
|
if (exportEmbedScene) {
|
||||||
try {
|
try {
|
||||||
@ -163,7 +178,7 @@ export const exportToSvg = async (
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
|
const [minX, minY, width, height] = getCanvasSize(elements, padding);
|
||||||
|
|
||||||
// initialize SVG root
|
// initialize SVG root
|
||||||
const svgRoot = document.createElementNS(SVG_NS, "svg");
|
const svgRoot = document.createElementNS(SVG_NS, "svg");
|
||||||
@ -198,8 +213,8 @@ export const exportToSvg = async (
|
|||||||
|
|
||||||
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
||||||
|
|
||||||
const offsetX = -minX + (onlyExportingSingleFrame ? 0 : exportPadding);
|
const offsetX = -minX + (onlyExportingSingleFrame ? 0 : padding);
|
||||||
const offsetY = -minY + (onlyExportingSingleFrame ? 0 : exportPadding);
|
const offsetY = -minY + (onlyExportingSingleFrame ? 0 : padding);
|
||||||
|
|
||||||
const exportingFrame =
|
const exportingFrame =
|
||||||
isExportingWholeCanvas || !onlyExportingSingleFrame
|
isExportingWholeCanvas || !onlyExportingSingleFrame
|
||||||
@ -243,13 +258,24 @@ export const exportToSvg = async (
|
|||||||
|
|
||||||
// render background rect
|
// render background rect
|
||||||
if (appState.exportBackground && viewBackgroundColor) {
|
if (appState.exportBackground && viewBackgroundColor) {
|
||||||
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
|
if (appState.fancyBackgroundImageUrl) {
|
||||||
rect.setAttribute("x", "0");
|
await applyFancyBackgroundOnSvg({
|
||||||
rect.setAttribute("y", "0");
|
svgRoot,
|
||||||
rect.setAttribute("width", `${width}`);
|
fancyBackgroundImageUrl:
|
||||||
rect.setAttribute("height", `${height}`);
|
`${appState.fancyBackgroundImageUrl}` as DataURL,
|
||||||
rect.setAttribute("fill", viewBackgroundColor);
|
backgroundColor: viewBackgroundColor,
|
||||||
svgRoot.appendChild(rect);
|
dimensions: { w: width, h: height },
|
||||||
|
exportScale,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
|
||||||
|
rect.setAttribute("x", "0");
|
||||||
|
rect.setAttribute("y", "0");
|
||||||
|
rect.setAttribute("width", `${width}`);
|
||||||
|
rect.setAttribute("height", `${height}`);
|
||||||
|
rect.setAttribute("fill", viewBackgroundColor);
|
||||||
|
svgRoot.appendChild(rect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsvg = rough.svg(svgRoot);
|
const rsvg = rough.svg(svgRoot);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FANCY_BG_BORDER_RADIUS, FANCY_BG_PADDING } from "../constants";
|
import { FANCY_BG_BORDER_RADIUS, FANCY_BG_PADDING, SVG_NS } from "../constants";
|
||||||
import { loadHTMLImageElement } from "../element/image";
|
import { loadHTMLImageElement, loadSVGElement } from "../element/image";
|
||||||
import { roundRect } from "../renderer/roundRect";
|
import { roundRect } from "../renderer/roundRect";
|
||||||
import { AppState, DataURL } from "../types";
|
import { AppState, DataURL } from "../types";
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ const addContentBackground = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const applyFancyBackground = async ({
|
export const applyFancyBackgroundOnCanvas = async ({
|
||||||
canvas,
|
canvas,
|
||||||
fancyBackgroundImageUrl,
|
fancyBackgroundImageUrl,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
@ -144,3 +144,45 @@ export const applyFancyBackground = async ({
|
|||||||
|
|
||||||
addContentBackground(context, canvasDimensions, backgroundColor, exportScale);
|
addContentBackground(context, canvasDimensions, backgroundColor, exportScale);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const applyFancyBackgroundOnSvg = async ({
|
||||||
|
svgRoot,
|
||||||
|
fancyBackgroundImageUrl,
|
||||||
|
backgroundColor,
|
||||||
|
dimensions,
|
||||||
|
exportScale,
|
||||||
|
}: {
|
||||||
|
svgRoot: SVGSVGElement;
|
||||||
|
fancyBackgroundImageUrl: DataURL;
|
||||||
|
backgroundColor: string;
|
||||||
|
dimensions: Dimensions;
|
||||||
|
exportScale: AppState["exportScale"];
|
||||||
|
}) => {
|
||||||
|
const fancyBackgroundImage = await loadSVGElement(
|
||||||
|
`${fancyBackgroundImageUrl}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
fancyBackgroundImage.setAttribute("x", "0");
|
||||||
|
fancyBackgroundImage.setAttribute("y", "0");
|
||||||
|
fancyBackgroundImage.setAttribute("width", `${dimensions.w}`);
|
||||||
|
fancyBackgroundImage.setAttribute("height", `${dimensions.h}`);
|
||||||
|
fancyBackgroundImage.setAttribute("preserveAspectRatio", "none");
|
||||||
|
|
||||||
|
svgRoot.appendChild(fancyBackgroundImage);
|
||||||
|
|
||||||
|
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
|
||||||
|
rect.setAttribute("x", (FANCY_BG_PADDING * exportScale).toString());
|
||||||
|
rect.setAttribute("y", (FANCY_BG_PADDING * exportScale).toString());
|
||||||
|
rect.setAttribute(
|
||||||
|
"width",
|
||||||
|
`${dimensions.w - FANCY_BG_PADDING * 2 * exportScale}`,
|
||||||
|
);
|
||||||
|
rect.setAttribute(
|
||||||
|
"height",
|
||||||
|
`${dimensions.h - FANCY_BG_PADDING * 2 * exportScale}`,
|
||||||
|
);
|
||||||
|
rect.setAttribute("rx", (FANCY_BG_PADDING * exportScale).toString());
|
||||||
|
rect.setAttribute("ry", (FANCY_BG_PADDING * exportScale).toString());
|
||||||
|
rect.setAttribute("fill", backgroundColor);
|
||||||
|
svgRoot.appendChild(rect);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user