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