feat: render scene after loading bcg image in export
This commit is contained in:
parent
fe17d88b74
commit
e6ae8177ab
@ -22,7 +22,7 @@ import {
|
|||||||
} from "../element";
|
} from "../element";
|
||||||
|
|
||||||
import { roundRect } from "./roundRect";
|
import { roundRect } from "./roundRect";
|
||||||
import { RenderConfig } from "../scene/types";
|
import { RenderConfig, ScrollBars } from "../scene/types";
|
||||||
import {
|
import {
|
||||||
getScrollBars,
|
getScrollBars,
|
||||||
SCROLLBAR_COLOR,
|
SCROLLBAR_COLOR,
|
||||||
@ -386,7 +386,7 @@ const frameClip = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addExportBackground = (
|
const addExportBackground = (
|
||||||
canvas: HTMLCanvasElement,
|
context: CanvasRenderingContext2D,
|
||||||
normalizedCanvasWidth: number,
|
normalizedCanvasWidth: number,
|
||||||
normalizedCanvasHeight: number,
|
normalizedCanvasHeight: number,
|
||||||
svgUrl: string,
|
svgUrl: string,
|
||||||
@ -396,18 +396,16 @@ const addExportBackground = (
|
|||||||
const MARGIN = 24;
|
const MARGIN = 24;
|
||||||
const BORDER_RADIUS = 12;
|
const BORDER_RADIUS = 12;
|
||||||
|
|
||||||
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
|
|
||||||
|
|
||||||
// Create a new image object
|
// Create a new image object
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
|
||||||
// When the image has loaded
|
// When the image has loaded
|
||||||
img.onload = (): void => {
|
img.onload = (): void => {
|
||||||
// Scale image to fill canvas and draw it onto the canvas
|
// Scale image to fill canvas and draw it onto the canvas
|
||||||
ctx.save();
|
context.save();
|
||||||
ctx.beginPath();
|
context.beginPath();
|
||||||
if (ctx.roundRect) {
|
if (context.roundRect) {
|
||||||
ctx.roundRect(
|
context.roundRect(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
normalizedCanvasWidth,
|
normalizedCanvasWidth,
|
||||||
@ -416,7 +414,7 @@ const addExportBackground = (
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
roundRect(
|
roundRect(
|
||||||
ctx,
|
context,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
normalizedCanvasWidth,
|
normalizedCanvasWidth,
|
||||||
@ -430,10 +428,10 @@ const addExportBackground = (
|
|||||||
);
|
);
|
||||||
const x = (normalizedCanvasWidth - img.width * scale) / 2;
|
const x = (normalizedCanvasWidth - img.width * scale) / 2;
|
||||||
const y = (normalizedCanvasHeight - img.height * scale) / 2;
|
const y = (normalizedCanvasHeight - img.height * scale) / 2;
|
||||||
ctx.clip();
|
context.clip();
|
||||||
ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
|
context.drawImage(img, x, y, img.width * scale, img.height * scale);
|
||||||
ctx.closePath();
|
context.closePath();
|
||||||
ctx.restore();
|
context.restore();
|
||||||
|
|
||||||
// Create shadow similar to the CSS box-shadow
|
// Create shadow similar to the CSS box-shadow
|
||||||
const shadows = [
|
const shadows = [
|
||||||
@ -459,15 +457,15 @@ const addExportBackground = (
|
|||||||
];
|
];
|
||||||
|
|
||||||
shadows.forEach((shadow, index): void => {
|
shadows.forEach((shadow, index): void => {
|
||||||
ctx.save();
|
context.save();
|
||||||
ctx.beginPath();
|
context.beginPath();
|
||||||
ctx.shadowColor = `rgba(0, 0, 0, ${shadow.alpha})`;
|
context.shadowColor = `rgba(0, 0, 0, ${shadow.alpha})`;
|
||||||
ctx.shadowBlur = shadow.blur;
|
context.shadowBlur = shadow.blur;
|
||||||
ctx.shadowOffsetX = shadow.offsetX;
|
context.shadowOffsetX = shadow.offsetX;
|
||||||
ctx.shadowOffsetY = shadow.offsetY;
|
context.shadowOffsetY = shadow.offsetY;
|
||||||
|
|
||||||
if (ctx.roundRect) {
|
if (context.roundRect) {
|
||||||
ctx.roundRect(
|
context.roundRect(
|
||||||
MARGIN,
|
MARGIN,
|
||||||
MARGIN,
|
MARGIN,
|
||||||
normalizedCanvasWidth - MARGIN * 2,
|
normalizedCanvasWidth - MARGIN * 2,
|
||||||
@ -476,7 +474,7 @@ const addExportBackground = (
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
roundRect(
|
roundRect(
|
||||||
ctx,
|
context,
|
||||||
MARGIN,
|
MARGIN,
|
||||||
MARGIN,
|
MARGIN,
|
||||||
normalizedCanvasWidth - MARGIN * 2,
|
normalizedCanvasWidth - MARGIN * 2,
|
||||||
@ -486,18 +484,18 @@ const addExportBackground = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (index === shadows.length - 1) {
|
if (index === shadows.length - 1) {
|
||||||
ctx.fillStyle = rectangleColor;
|
context.fillStyle = rectangleColor;
|
||||||
ctx.fill();
|
context.fill();
|
||||||
}
|
}
|
||||||
ctx.closePath();
|
context.closePath();
|
||||||
ctx.restore();
|
context.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset shadow properties for future drawings
|
// Reset shadow properties for future drawings
|
||||||
ctx.shadowColor = "transparent";
|
context.shadowColor = "transparent";
|
||||||
ctx.shadowBlur = 0;
|
context.shadowBlur = 0;
|
||||||
ctx.shadowOffsetX = 0;
|
context.shadowOffsetX = 0;
|
||||||
ctx.shadowOffsetY = 0;
|
context.shadowOffsetY = 0;
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
@ -511,6 +509,52 @@ const addExportBackground = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const paintBackground = async (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
normalizedCanvasWidth: number,
|
||||||
|
normalizedCanvasHeight: number,
|
||||||
|
{
|
||||||
|
viewBackgroundColor,
|
||||||
|
isExporting,
|
||||||
|
exportBackgroundImage,
|
||||||
|
}: Pick<
|
||||||
|
RenderConfig,
|
||||||
|
"viewBackgroundColor" | "isExporting" | "exportBackgroundImage"
|
||||||
|
>,
|
||||||
|
): Promise<void> => {
|
||||||
|
if (typeof viewBackgroundColor === "string") {
|
||||||
|
const hasTransparence =
|
||||||
|
viewBackgroundColor === "transparent" ||
|
||||||
|
viewBackgroundColor.length === 5 || // #RGBA
|
||||||
|
viewBackgroundColor.length === 9 || // #RRGGBBA
|
||||||
|
/(hsla|rgba)\(/.test(viewBackgroundColor);
|
||||||
|
if (hasTransparence) {
|
||||||
|
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
||||||
|
}
|
||||||
|
context.save();
|
||||||
|
if (isExporting && exportBackgroundImage) {
|
||||||
|
try {
|
||||||
|
await addExportBackground(
|
||||||
|
context,
|
||||||
|
normalizedCanvasWidth,
|
||||||
|
normalizedCanvasHeight,
|
||||||
|
exportBackgroundImage,
|
||||||
|
viewBackgroundColor,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to add background:", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.fillStyle = viewBackgroundColor;
|
||||||
|
context.fillRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
} else {
|
||||||
|
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const _renderScene = ({
|
export const _renderScene = ({
|
||||||
elements,
|
elements,
|
||||||
appState,
|
appState,
|
||||||
@ -536,6 +580,7 @@ export const _renderScene = ({
|
|||||||
renderSelection = true,
|
renderSelection = true,
|
||||||
renderGrid = true,
|
renderGrid = true,
|
||||||
isExporting,
|
isExporting,
|
||||||
|
viewBackgroundColor,
|
||||||
exportBackgroundImage,
|
exportBackgroundImage,
|
||||||
} = renderConfig;
|
} = renderConfig;
|
||||||
|
|
||||||
@ -554,41 +599,19 @@ export const _renderScene = ({
|
|||||||
context.filter = THEME_FILTER;
|
context.filter = THEME_FILTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paint background
|
let renderOutput: {
|
||||||
if (typeof renderConfig.viewBackgroundColor === "string") {
|
atLeastOneVisibleElement: boolean;
|
||||||
const hasTransparence =
|
scrollBars: ScrollBars | undefined;
|
||||||
renderConfig.viewBackgroundColor === "transparent" ||
|
} = {
|
||||||
renderConfig.viewBackgroundColor.length === 5 || // #RGBA
|
atLeastOneVisibleElement: false,
|
||||||
renderConfig.viewBackgroundColor.length === 9 || // #RRGGBBA
|
scrollBars: undefined,
|
||||||
/(hsla|rgba)\(/.test(renderConfig.viewBackgroundColor);
|
};
|
||||||
if (hasTransparence) {
|
|
||||||
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
paintBackground(context, normalizedCanvasWidth, normalizedCanvasHeight, {
|
||||||
}
|
isExporting,
|
||||||
context.save();
|
viewBackgroundColor,
|
||||||
if (isExporting && exportBackgroundImage) {
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
await addExportBackground(
|
|
||||||
canvas,
|
|
||||||
normalizedCanvasWidth,
|
|
||||||
normalizedCanvasHeight,
|
|
||||||
exportBackgroundImage,
|
exportBackgroundImage,
|
||||||
renderConfig.viewBackgroundColor!,
|
}).then(() => {
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to add background:", error);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
} else {
|
|
||||||
context.fillStyle = renderConfig.viewBackgroundColor;
|
|
||||||
context.fillRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.restore();
|
|
||||||
} else {
|
|
||||||
context.clearRect(0, 0, normalizedCanvasWidth, normalizedCanvasHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply zoom
|
// Apply zoom
|
||||||
context.save();
|
context.save();
|
||||||
context.scale(renderConfig.zoom.value, renderConfig.zoom.value);
|
context.scale(renderConfig.zoom.value, renderConfig.zoom.value);
|
||||||
@ -608,13 +631,18 @@ export const _renderScene = ({
|
|||||||
|
|
||||||
// Paint visible elements
|
// Paint visible elements
|
||||||
const visibleElements = elements.filter((element) =>
|
const visibleElements = elements.filter((element) =>
|
||||||
isVisibleElement(element, normalizedCanvasWidth, normalizedCanvasHeight, {
|
isVisibleElement(
|
||||||
|
element,
|
||||||
|
normalizedCanvasWidth,
|
||||||
|
normalizedCanvasHeight,
|
||||||
|
{
|
||||||
zoom: renderConfig.zoom,
|
zoom: renderConfig.zoom,
|
||||||
offsetLeft: appState.offsetLeft,
|
offsetLeft: appState.offsetLeft,
|
||||||
offsetTop: appState.offsetTop,
|
offsetTop: appState.offsetTop,
|
||||||
scrollX: renderConfig.scrollX,
|
scrollX: renderConfig.scrollX,
|
||||||
scrollY: renderConfig.scrollY,
|
scrollY: renderConfig.scrollY,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupsToBeAddedToFrame = new Set<string>();
|
const groupsToBeAddedToFrame = new Set<string>();
|
||||||
@ -635,8 +663,9 @@ export const _renderScene = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let editingLinearElement: NonDeleted<ExcalidrawLinearElement> | undefined =
|
let editingLinearElement:
|
||||||
undefined;
|
| NonDeleted<ExcalidrawLinearElement>
|
||||||
|
| undefined = undefined;
|
||||||
|
|
||||||
visibleElements
|
visibleElements
|
||||||
.filter((el) => !isEmbeddableOrFrameLabel(el))
|
.filter((el) => !isEmbeddableOrFrameLabel(el))
|
||||||
@ -790,7 +819,8 @@ export const _renderScene = ({
|
|||||||
// correct element from visible elements
|
// correct element from visible elements
|
||||||
if (
|
if (
|
||||||
locallySelectedElements.length === 1 &&
|
locallySelectedElements.length === 1 &&
|
||||||
appState.editingLinearElement?.elementId === locallySelectedElements[0].id
|
appState.editingLinearElement?.elementId ===
|
||||||
|
locallySelectedElements[0].id
|
||||||
) {
|
) {
|
||||||
renderLinearPointHandles(
|
renderLinearPointHandles(
|
||||||
context,
|
context,
|
||||||
@ -1139,7 +1169,13 @@ export const _renderScene = ({
|
|||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
|
renderOutput = {
|
||||||
|
atLeastOneVisibleElement: visibleElements.length > 0,
|
||||||
|
scrollBars,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSceneThrottled = throttleRAF(
|
const renderSceneThrottled = throttleRAF(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user