|
@@ -1,4 +1,4 @@
|
|
|
-import React, { useEffect, useReducer, useRef, useState } from "react";
|
|
|
+import React, { useEffect, useReducer, useRef } from "react";
|
|
|
import clsx from "clsx";
|
|
|
|
|
|
import type { ActionManager } from "../actions/manager";
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
FANCY_BACKGROUND_IMAGES,
|
|
|
} from "../constants";
|
|
|
|
|
|
-import { canvasToBlob } from "../data/blob";
|
|
|
import { nativeFileSystemSupported } from "../data/filesystem";
|
|
|
import {
|
|
|
ExcalidrawElement,
|
|
@@ -29,7 +28,7 @@ import {
|
|
|
} from "../element/types";
|
|
|
import { t } from "../i18n";
|
|
|
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
|
|
-import { exportToCanvas, getScaleToFit } from "../packages/utils";
|
|
|
+import { getScaleToFit } from "../packages/utils";
|
|
|
|
|
|
import { copyIcon, downloadIcon, helpIcon } from "./icons";
|
|
|
import { Dialog } from "./Dialog";
|
|
@@ -50,6 +49,7 @@ import {
|
|
|
import { getFancyBackgroundPadding } from "../scene/fancyBackground";
|
|
|
import { Select } from "./Select";
|
|
|
import { Bounds } from "../element/bounds";
|
|
|
+import { CanvasPreview } from "./ImageExportPreview";
|
|
|
|
|
|
const supportsContextFilters =
|
|
|
"filter" in document.createElement("canvas").getContext("2d")!;
|
|
@@ -61,18 +61,6 @@ const fancyBackgroundImageOptions = Object.entries(FANCY_BACKGROUND_IMAGES).map(
|
|
|
}),
|
|
|
);
|
|
|
|
|
|
-export const ErrorCanvasPreview = () => {
|
|
|
- return (
|
|
|
- <div>
|
|
|
- <h3>{t("canvasError.cannotShowPreview")}</h3>
|
|
|
- <p>
|
|
|
- <span>{t("canvasError.canvasTooBig")}</span>
|
|
|
- </p>
|
|
|
- <em>({t("canvasError.canvasTooBigTip")})</em>
|
|
|
- </div>
|
|
|
- );
|
|
|
-};
|
|
|
-
|
|
|
type ImageExportModalProps = {
|
|
|
appState: UIAppState;
|
|
|
elements: readonly NonDeletedExcalidrawElement[];
|
|
@@ -228,7 +216,6 @@ const ImageExportModal = ({
|
|
|
const appProps = useAppProps();
|
|
|
|
|
|
const previewRef = useRef<HTMLDivElement>(null);
|
|
|
- const [renderError, setRenderError] = useState<Error | null>(null);
|
|
|
|
|
|
// Upscale exported image when is smaller than preview
|
|
|
useEffect(() => {
|
|
@@ -281,91 +268,17 @@ const ImageExportModal = ({
|
|
|
actionManager,
|
|
|
]);
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- const previewNode = previewRef.current;
|
|
|
- if (!previewNode) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const maxWidth = previewNode.offsetWidth;
|
|
|
- const maxHeight = previewNode.offsetHeight;
|
|
|
-
|
|
|
- const maxWidthOrHeight = Math.min(maxWidth, maxHeight);
|
|
|
-
|
|
|
- if (!maxWidth) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // when switching between solid/no background and image background, we clear the canvas to prevent flickering
|
|
|
- const isExportWithFancyBackground =
|
|
|
- appState.exportBackground && appState.fancyBackgroundImageKey !== "solid";
|
|
|
-
|
|
|
- if (state.isExportWithFancyBackground !== isExportWithFancyBackground) {
|
|
|
- const existingCanvas = previewNode.querySelector("canvas");
|
|
|
- if (existingCanvas) {
|
|
|
- const context = existingCanvas.getContext("2d");
|
|
|
-
|
|
|
- context!.clearRect(0, 0, existingCanvas.width, existingCanvas.height);
|
|
|
- }
|
|
|
- dispatch({
|
|
|
- type: "SET_IS_EXPORT_WITH_FANCY_BACKGROUND",
|
|
|
- isExportWithFancyBackground,
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- exportToCanvas({
|
|
|
- elements: state.exportedElements,
|
|
|
- appState,
|
|
|
- files,
|
|
|
- exportPadding: DEFAULT_EXPORT_PADDING,
|
|
|
- maxWidthOrHeight,
|
|
|
- })
|
|
|
- .then((canvas) => {
|
|
|
- setRenderError(null);
|
|
|
- // if converting to blob fails, there's some problem that will
|
|
|
- // likely prevent preview and export (e.g. canvas too big)
|
|
|
- return canvasToBlob(canvas).then(() => {
|
|
|
- const existingCanvas = previewNode.querySelector("canvas");
|
|
|
- if (!existingCanvas) {
|
|
|
- previewNode.appendChild(canvas);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- existingCanvas.width = canvas.width;
|
|
|
- existingCanvas.height = canvas.height;
|
|
|
-
|
|
|
- const context = existingCanvas.getContext("2d");
|
|
|
- context!.drawImage(canvas, 0, 0);
|
|
|
- });
|
|
|
-
|
|
|
- // Get the 2D rendering context of the existing canvas
|
|
|
- })
|
|
|
- .catch((error) => {
|
|
|
- console.error(error);
|
|
|
- setRenderError(error);
|
|
|
- });
|
|
|
- }, [
|
|
|
- appState,
|
|
|
- appState.exportBackground,
|
|
|
- appState.fancyBackgroundImageKey,
|
|
|
- files,
|
|
|
- state.exportedElements,
|
|
|
- state.isExportWithFancyBackground,
|
|
|
- ]);
|
|
|
-
|
|
|
return (
|
|
|
<div className="ImageExportModal">
|
|
|
<h3>{t("imageExportDialog.header")}</h3>
|
|
|
<div className="ImageExportModal__preview">
|
|
|
- <div
|
|
|
- className={clsx("ImageExportModal__preview__canvas", {
|
|
|
- "ImageExportModal__preview__canvas--img-bcg":
|
|
|
- appState.exportBackground &&
|
|
|
- appState.fancyBackgroundImageKey !== "solid",
|
|
|
- })}
|
|
|
- ref={previewRef}
|
|
|
- >
|
|
|
- {renderError && <ErrorCanvasPreview />}
|
|
|
- </div>
|
|
|
+ <React.Suspense fallback={<div>Loading...</div>}>
|
|
|
+ <CanvasPreview
|
|
|
+ appState={appState}
|
|
|
+ files={files}
|
|
|
+ elements={state.exportedElements}
|
|
|
+ />
|
|
|
+ </React.Suspense>
|
|
|
</div>
|
|
|
<div className="ImageExportModal__settings">
|
|
|
<h3>{t("imageExportDialog.header")}</h3>
|