feat: separate fancyBackground from renderScene
This commit is contained in:
parent
3c56efb9fe
commit
d6515e26b9
@ -72,7 +72,7 @@ export const getDefaultAppState = (): Omit<
|
||||
openMenu: null,
|
||||
openPopup: null,
|
||||
openSidebar: null,
|
||||
openDialog: null,
|
||||
openDialog: "imageExport",
|
||||
pasteDialog: { shown: false, data: null },
|
||||
previousSelectedElementIds: {},
|
||||
resizingElement: null,
|
||||
@ -101,7 +101,7 @@ export const getDefaultAppState = (): Omit<
|
||||
pendingImageElementId: null,
|
||||
showHyperlinkPopup: false,
|
||||
selectedLinearElement: null,
|
||||
exportBackgroundImage:
|
||||
fancyBackgroundImageUrl:
|
||||
EXPORT_BACKGROUND_IMAGES[DEFAULT_EXPORT_BACKGROUND_IMAGE].path,
|
||||
};
|
||||
};
|
||||
|
@ -138,7 +138,7 @@ const ImageExportModal = ({
|
||||
}, [
|
||||
appState,
|
||||
appState.exportBackground,
|
||||
appState.exportBackgroundImage,
|
||||
appState.fancyBackgroundImageUrl,
|
||||
files,
|
||||
exportedElements,
|
||||
]);
|
||||
@ -150,7 +150,7 @@ const ImageExportModal = ({
|
||||
<div
|
||||
className={clsx("ImageExportModal__preview__canvas", {
|
||||
"ImageExportModal__preview__canvas--img-bcg":
|
||||
appState.exportBackground && appState.exportBackgroundImage,
|
||||
appState.exportBackground && appState.fancyBackgroundImageUrl,
|
||||
})}
|
||||
ref={previewRef}
|
||||
>
|
||||
@ -159,8 +159,8 @@ const ImageExportModal = ({
|
||||
</div>
|
||||
<div className="ImageExportModal__settings">
|
||||
<h3>{t("imageExportDialog.header")}</h3>
|
||||
<div className="ImageExportModal__settings__filename">
|
||||
{!nativeFileSystemSupported && (
|
||||
<div className="ImageExportModal__settings__filename">
|
||||
<input
|
||||
type="text"
|
||||
className="TextInput"
|
||||
@ -177,8 +177,8 @@ const ImageExportModal = ({
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{someElementIsSelected && (
|
||||
<ExportSetting
|
||||
label={t("imageExportDialog.label.onlySelected")}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import cssVariables from "./css/variables.module.scss";
|
||||
import { AppProps } from "./types";
|
||||
import { AppProps, DataURL } from "./types";
|
||||
import { ExcalidrawElement, FontFamilyValues } from "./element/types";
|
||||
import { COLOR_PALETTE } from "./colors";
|
||||
|
||||
@ -321,11 +321,14 @@ export const LIBRARY_DISABLED_TYPES = new Set(["embeddable", "image"] as const);
|
||||
|
||||
export const EXPORT_BACKGROUND_IMAGES = {
|
||||
solid: { path: null, label: "solid color" },
|
||||
bubbles: { path: "/backgrounds/bubbles.svg", label: "bubbles" },
|
||||
bubbles2: { path: "/backgrounds/bubbles2.svg", label: "bubbles 2" },
|
||||
bricks: { path: "/backgrounds/bricks.svg", label: "bricks" },
|
||||
lines: { path: "/backgrounds/lines.svg", label: "lines" },
|
||||
lines2: { path: "/backgrounds/lines2.svg", label: "lines 2" },
|
||||
bubbles: { path: "/backgrounds/bubbles.svg" as DataURL, label: "bubbles" },
|
||||
bubbles2: {
|
||||
path: "/backgrounds/bubbles2.svg" as DataURL,
|
||||
label: "bubbles 2",
|
||||
},
|
||||
bricks: { path: "/backgrounds/bricks.svg" as DataURL, label: "bricks" },
|
||||
lines: { path: "/backgrounds/lines.svg" as DataURL, label: "lines" },
|
||||
lines2: { path: "/backgrounds/lines2.svg" as DataURL, label: "lines 2" },
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_EXPORT_BACKGROUND_IMAGE: keyof typeof EXPORT_BACKGROUND_IMAGES =
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
updateImageCache,
|
||||
} from "../element/image";
|
||||
import Scene from "./Scene";
|
||||
import { applyFancyBackground } from "./fancyBackground";
|
||||
|
||||
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
|
||||
|
||||
@ -54,6 +55,14 @@ export const exportToCanvas = async (
|
||||
|
||||
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
||||
|
||||
if (appState.fancyBackgroundImageUrl) {
|
||||
await applyFancyBackground(
|
||||
canvas,
|
||||
appState.fancyBackgroundImageUrl,
|
||||
viewBackgroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
renderStaticScene({
|
||||
canvas,
|
||||
rc: rough.canvas(canvas),
|
||||
|
147
src/scene/fancyBackground.ts
Normal file
147
src/scene/fancyBackground.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import { EXPORT_BG_BORDER_RADIUS, EXPORT_BG_PADDING } from "../constants";
|
||||
import { loadHTMLImageElement } from "../element/image";
|
||||
import { roundRect } from "../renderer/roundRect";
|
||||
import { DataURL } from "../types";
|
||||
|
||||
type Dimensions = { w: number; h: number };
|
||||
|
||||
const getScaleToFill = (contentSize: Dimensions, containerSize: Dimensions) => {
|
||||
const scale = Math.max(
|
||||
containerSize.w / contentSize.w,
|
||||
containerSize.h / contentSize.h,
|
||||
);
|
||||
|
||||
return scale;
|
||||
};
|
||||
|
||||
const getScaleToFit = (contentSize: Dimensions, containerSize: Dimensions) => {
|
||||
const scale = Math.min(
|
||||
containerSize.w / contentSize.w,
|
||||
containerSize.h / contentSize.h,
|
||||
);
|
||||
|
||||
return scale;
|
||||
};
|
||||
|
||||
const addImageBackground = (
|
||||
context: CanvasRenderingContext2D,
|
||||
canvasWidth: number,
|
||||
canvasHeight: number,
|
||||
fancyBackgroundImage: HTMLImageElement,
|
||||
) => {
|
||||
context.save();
|
||||
context.beginPath();
|
||||
if (context.roundRect) {
|
||||
context.roundRect(0, 0, canvasWidth, canvasHeight, EXPORT_BG_BORDER_RADIUS);
|
||||
} else {
|
||||
roundRect(
|
||||
context,
|
||||
0,
|
||||
0,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
EXPORT_BG_BORDER_RADIUS,
|
||||
);
|
||||
}
|
||||
const scale = getScaleToFill(
|
||||
{ w: fancyBackgroundImage.width, h: fancyBackgroundImage.height },
|
||||
{ w: canvasWidth, h: canvasHeight },
|
||||
);
|
||||
const x = (canvasWidth - fancyBackgroundImage.width * scale) / 2;
|
||||
const y = (canvasHeight - fancyBackgroundImage.height * scale) / 2;
|
||||
context.clip();
|
||||
context.drawImage(
|
||||
fancyBackgroundImage,
|
||||
x,
|
||||
y,
|
||||
fancyBackgroundImage.width * scale,
|
||||
fancyBackgroundImage.height * scale,
|
||||
);
|
||||
context.closePath();
|
||||
context.restore();
|
||||
};
|
||||
|
||||
const addContentBackground = (
|
||||
context: CanvasRenderingContext2D,
|
||||
canvasWidth: number,
|
||||
canvasHeight: number,
|
||||
contentBackgroundColor: string,
|
||||
) => {
|
||||
const shadows = [
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 0.7698959708213806,
|
||||
blur: 1.4945039749145508,
|
||||
alpha: 0.02,
|
||||
},
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 1.1299999952316284,
|
||||
blur: 4.1321120262146,
|
||||
alpha: 0.04,
|
||||
},
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 4.130000114440918,
|
||||
blur: 9.94853401184082,
|
||||
alpha: 0.05,
|
||||
},
|
||||
{ offsetX: 0, offsetY: 13, blur: 33, alpha: 0.07 },
|
||||
];
|
||||
|
||||
shadows.forEach((shadow, index): void => {
|
||||
context.save();
|
||||
context.beginPath();
|
||||
context.shadowColor = `rgba(0, 0, 0, ${shadow.alpha})`;
|
||||
context.shadowBlur = shadow.blur;
|
||||
context.shadowOffsetX = shadow.offsetX;
|
||||
context.shadowOffsetY = shadow.offsetY;
|
||||
|
||||
if (context.roundRect) {
|
||||
context.roundRect(
|
||||
EXPORT_BG_PADDING,
|
||||
EXPORT_BG_PADDING,
|
||||
canvasWidth - EXPORT_BG_PADDING * 2,
|
||||
canvasHeight - EXPORT_BG_PADDING * 2,
|
||||
EXPORT_BG_BORDER_RADIUS,
|
||||
);
|
||||
} else {
|
||||
roundRect(
|
||||
context,
|
||||
EXPORT_BG_PADDING,
|
||||
EXPORT_BG_PADDING,
|
||||
canvasWidth - EXPORT_BG_PADDING * 2,
|
||||
canvasHeight - EXPORT_BG_PADDING * 2,
|
||||
EXPORT_BG_BORDER_RADIUS,
|
||||
);
|
||||
}
|
||||
|
||||
if (index === shadows.length - 1) {
|
||||
context.fillStyle = contentBackgroundColor;
|
||||
context.fill();
|
||||
}
|
||||
context.closePath();
|
||||
context.restore();
|
||||
});
|
||||
};
|
||||
|
||||
export const applyFancyBackground = async (
|
||||
canvas: HTMLCanvasElement,
|
||||
fancyBackgroundImageUrl: DataURL,
|
||||
backgroundColor: string,
|
||||
) => {
|
||||
const context = canvas.getContext("2d")!;
|
||||
|
||||
const fancyBackgroundImage = await loadHTMLImageElement(
|
||||
fancyBackgroundImageUrl,
|
||||
);
|
||||
|
||||
addImageBackground(
|
||||
context,
|
||||
canvas.width,
|
||||
canvas.height,
|
||||
fancyBackgroundImage,
|
||||
);
|
||||
|
||||
addContentBackground(context, canvas.width, canvas.height, backgroundColor);
|
||||
};
|
@ -287,7 +287,7 @@ export type AppState = {
|
||||
pendingImageElementId: ExcalidrawImageElement["id"] | null;
|
||||
showHyperlinkPopup: false | "info" | "editor";
|
||||
selectedLinearElement: LinearElementEditor | null;
|
||||
exportBackgroundImage: string | null;
|
||||
fancyBackgroundImageUrl: DataURL | null;
|
||||
};
|
||||
|
||||
export type UIAppState = Omit<
|
||||
|
Loading…
x
Reference in New Issue
Block a user