Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f32b7c1ef | ||
|
|
b885d87bc5 | ||
|
|
c170403b13 | ||
|
|
705ac9c1ab | ||
|
|
68692b9d4c |
@@ -17,7 +17,7 @@
|
|||||||
An open source virtual hand-drawn style whiteboard. </br>
|
An open source virtual hand-drawn style whiteboard. </br>
|
||||||
Collaborative and end-to-end encrypted. </br>
|
Collaborative and end-to-end encrypted. </br>
|
||||||
<br />
|
<br />
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dwelle/tunnel-rat": "0.1.1",
|
"@dwelle/tunnel-rat": "0.1.1",
|
||||||
|
"@excalidraw/roughjs": "4.5.3",
|
||||||
"@sentry/browser": "6.2.5",
|
"@sentry/browser": "6.2.5",
|
||||||
"@sentry/integrations": "6.2.5",
|
"@sentry/integrations": "6.2.5",
|
||||||
"@testing-library/jest-dom": "5.16.2",
|
"@testing-library/jest-dom": "5.16.2",
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@
|
|||||||
// if its present redirect
|
// if its present redirect
|
||||||
document.cookie.includes("excplus-autoredirect=true")
|
document.cookie.includes("excplus-autoredirect=true")
|
||||||
) {
|
) {
|
||||||
window.location.href = "https://app.excalidraw.com";
|
// window.location.href = "https://app.excalidraw.com";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
} from "../element/types";
|
} from "../element/types";
|
||||||
import { getSelectedElements } from "../scene";
|
import { getSelectedElements } from "../scene";
|
||||||
|
import { AppState } from "../types";
|
||||||
import { getFontString } from "../utils";
|
import { getFontString } from "../utils";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
|
|
||||||
@@ -185,107 +186,110 @@ export const actionCreateContainerFromText = register({
|
|||||||
trackEvent: { category: "element" },
|
trackEvent: { category: "element" },
|
||||||
predicate: (elements, appState) => {
|
predicate: (elements, appState) => {
|
||||||
const selectedElements = getSelectedElements(elements, appState);
|
const selectedElements = getSelectedElements(elements, appState);
|
||||||
return selectedElements.length === 1 && isTextElement(selectedElements[0]);
|
const areTextElements = selectedElements.every((el) => isTextElement(el));
|
||||||
|
return selectedElements.length > 0 && areTextElements;
|
||||||
},
|
},
|
||||||
perform: (elements, appState) => {
|
perform: (elements, appState) => {
|
||||||
const selectedElements = getSelectedElements(
|
const selectedElements = getSelectedElements(
|
||||||
getNonDeletedElements(elements),
|
getNonDeletedElements(elements),
|
||||||
appState,
|
appState,
|
||||||
);
|
);
|
||||||
const updatedElements = elements.slice();
|
let updatedElements: readonly ExcalidrawElement[] = elements.slice();
|
||||||
if (selectedElements.length === 1 && isTextElement(selectedElements[0])) {
|
const containerIds: AppState["selectedElementIds"] = {};
|
||||||
const textElement = selectedElements[0];
|
|
||||||
const container = newElement({
|
|
||||||
type: "rectangle",
|
|
||||||
backgroundColor: appState.currentItemBackgroundColor,
|
|
||||||
boundElements: [
|
|
||||||
...(textElement.boundElements || []),
|
|
||||||
{ id: textElement.id, type: "text" },
|
|
||||||
],
|
|
||||||
angle: textElement.angle,
|
|
||||||
fillStyle: appState.currentItemFillStyle,
|
|
||||||
strokeColor: appState.currentItemStrokeColor,
|
|
||||||
roughness: appState.currentItemRoughness,
|
|
||||||
strokeWidth: appState.currentItemStrokeWidth,
|
|
||||||
strokeStyle: appState.currentItemStrokeStyle,
|
|
||||||
roundness:
|
|
||||||
appState.currentItemRoundness === "round"
|
|
||||||
? {
|
|
||||||
type: isUsingAdaptiveRadius("rectangle")
|
|
||||||
? ROUNDNESS.ADAPTIVE_RADIUS
|
|
||||||
: ROUNDNESS.PROPORTIONAL_RADIUS,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
opacity: 100,
|
|
||||||
locked: false,
|
|
||||||
x: textElement.x - BOUND_TEXT_PADDING,
|
|
||||||
y: textElement.y - BOUND_TEXT_PADDING,
|
|
||||||
width: computeContainerDimensionForBoundText(
|
|
||||||
textElement.width,
|
|
||||||
"rectangle",
|
|
||||||
),
|
|
||||||
height: computeContainerDimensionForBoundText(
|
|
||||||
textElement.height,
|
|
||||||
"rectangle",
|
|
||||||
),
|
|
||||||
groupIds: textElement.groupIds,
|
|
||||||
});
|
|
||||||
|
|
||||||
// update bindings
|
for (const textElement of selectedElements) {
|
||||||
if (textElement.boundElements?.length) {
|
if (isTextElement(textElement)) {
|
||||||
const linearElementIds = textElement.boundElements
|
const container = newElement({
|
||||||
.filter((ele) => ele.type === "arrow")
|
type: "rectangle",
|
||||||
.map((el) => el.id);
|
backgroundColor: appState.currentItemBackgroundColor,
|
||||||
const linearElements = updatedElements.filter((ele) =>
|
boundElements: [
|
||||||
linearElementIds.includes(ele.id),
|
...(textElement.boundElements || []),
|
||||||
) as ExcalidrawLinearElement[];
|
{ id: textElement.id, type: "text" },
|
||||||
linearElements.forEach((ele) => {
|
],
|
||||||
let startBinding = ele.startBinding;
|
angle: textElement.angle,
|
||||||
let endBinding = ele.endBinding;
|
fillStyle: appState.currentItemFillStyle,
|
||||||
|
strokeColor: appState.currentItemStrokeColor,
|
||||||
if (startBinding?.elementId === textElement.id) {
|
roughness: appState.currentItemRoughness,
|
||||||
startBinding = {
|
strokeWidth: appState.currentItemStrokeWidth,
|
||||||
...startBinding,
|
strokeStyle: appState.currentItemStrokeStyle,
|
||||||
elementId: container.id,
|
roundness:
|
||||||
};
|
appState.currentItemRoundness === "round"
|
||||||
}
|
? {
|
||||||
|
type: isUsingAdaptiveRadius("rectangle")
|
||||||
if (endBinding?.elementId === textElement.id) {
|
? ROUNDNESS.ADAPTIVE_RADIUS
|
||||||
endBinding = { ...endBinding, elementId: container.id };
|
: ROUNDNESS.PROPORTIONAL_RADIUS,
|
||||||
}
|
}
|
||||||
|
: null,
|
||||||
if (startBinding || endBinding) {
|
opacity: 100,
|
||||||
mutateElement(ele, { startBinding, endBinding });
|
locked: false,
|
||||||
}
|
x: textElement.x - BOUND_TEXT_PADDING,
|
||||||
|
y: textElement.y - BOUND_TEXT_PADDING,
|
||||||
|
width: computeContainerDimensionForBoundText(
|
||||||
|
textElement.width,
|
||||||
|
"rectangle",
|
||||||
|
),
|
||||||
|
height: computeContainerDimensionForBoundText(
|
||||||
|
textElement.height,
|
||||||
|
"rectangle",
|
||||||
|
),
|
||||||
|
groupIds: textElement.groupIds,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
mutateElement(textElement, {
|
// update bindings
|
||||||
containerId: container.id,
|
if (textElement.boundElements?.length) {
|
||||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
const linearElementIds = textElement.boundElements
|
||||||
boundElements: null,
|
.filter((ele) => ele.type === "arrow")
|
||||||
});
|
.map((el) => el.id);
|
||||||
redrawTextBoundingBox(textElement, container);
|
const linearElements = updatedElements.filter((ele) =>
|
||||||
|
linearElementIds.includes(ele.id),
|
||||||
|
) as ExcalidrawLinearElement[];
|
||||||
|
linearElements.forEach((ele) => {
|
||||||
|
let startBinding = ele.startBinding;
|
||||||
|
let endBinding = ele.endBinding;
|
||||||
|
|
||||||
return {
|
if (startBinding?.elementId === textElement.id) {
|
||||||
elements: pushContainerBelowText(
|
startBinding = {
|
||||||
[...elements, container],
|
...startBinding,
|
||||||
|
elementId: container.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endBinding?.elementId === textElement.id) {
|
||||||
|
endBinding = { ...endBinding, elementId: container.id };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startBinding || endBinding) {
|
||||||
|
mutateElement(ele, { startBinding, endBinding }, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mutateElement(
|
||||||
|
textElement,
|
||||||
|
{
|
||||||
|
containerId: container.id,
|
||||||
|
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||||
|
boundElements: null,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
redrawTextBoundingBox(textElement, container);
|
||||||
|
|
||||||
|
updatedElements = pushContainerBelowText(
|
||||||
|
[...updatedElements, container],
|
||||||
container,
|
container,
|
||||||
textElement,
|
textElement,
|
||||||
),
|
);
|
||||||
appState: {
|
containerIds[container.id] = true;
|
||||||
...appState,
|
}
|
||||||
selectedElementIds: {
|
|
||||||
[container.id]: true,
|
|
||||||
[textElement.id]: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
commitToHistory: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
elements: updatedElements,
|
elements: updatedElements,
|
||||||
appState,
|
appState: {
|
||||||
|
...appState,
|
||||||
|
selectedElementIds: containerIds,
|
||||||
|
},
|
||||||
commitToHistory: true,
|
commitToHistory: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { flushSync } from "react-dom";
|
import { flushSync } from "react-dom";
|
||||||
|
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "@excalidraw/roughjs/bin/canvas";
|
||||||
import rough from "roughjs/bin/rough";
|
import rough from "@excalidraw/roughjs/bin/rough";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
ExcalidrawTextElementWithContainer,
|
ExcalidrawTextElementWithContainer,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { distance2d, rotate } from "../math";
|
import { distance2d, rotate } from "../math";
|
||||||
import rough from "roughjs/bin/rough";
|
import rough from "@excalidraw/roughjs/bin/rough";
|
||||||
import { Drawable, Op } from "roughjs/bin/core";
|
import { Drawable, Op } from "@excalidraw/roughjs/bin/core";
|
||||||
import { Point } from "../types";
|
import { Point } from "../types";
|
||||||
import {
|
import {
|
||||||
getShapeForElement,
|
getShapeForElement,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
|
|
||||||
import { getElementAbsoluteCoords, getCurvePathOps, Bounds } from "./bounds";
|
import { getElementAbsoluteCoords, getCurvePathOps, Bounds } from "./bounds";
|
||||||
import { Point } from "../types";
|
import { Point } from "../types";
|
||||||
import { Drawable } from "roughjs/bin/core";
|
import { Drawable } from "@excalidraw/roughjs/bin/core";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
import { getShapeForElement } from "../renderer/renderElement";
|
import { getShapeForElement } from "../renderer/renderElement";
|
||||||
import { hasBoundTextElement, isImageElement } from "./typeChecks";
|
import { hasBoundTextElement, isImageElement } from "./typeChecks";
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { Random } from "roughjs/bin/math";
|
import { Random } from "@excalidraw/roughjs/bin/math";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { isTestEnv } from "./utils";
|
import { isTestEnv } from "./utils";
|
||||||
|
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ import {
|
|||||||
getElementAbsoluteCoords,
|
getElementAbsoluteCoords,
|
||||||
getArrowheadPoints,
|
getArrowheadPoints,
|
||||||
} from "../element/bounds";
|
} from "../element/bounds";
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "@excalidraw/roughjs/bin/canvas";
|
||||||
import { Drawable, Options } from "roughjs/bin/core";
|
import { Drawable, Options } from "@excalidraw/roughjs/bin/core";
|
||||||
import { RoughSVG } from "roughjs/bin/svg";
|
import { RoughSVG } from "@excalidraw/roughjs/bin/svg";
|
||||||
import { RoughGenerator } from "roughjs/bin/generator";
|
import { RoughGenerator } from "@excalidraw/roughjs/bin/generator";
|
||||||
|
|
||||||
import { RenderConfig } from "../scene/types";
|
import { RenderConfig } from "../scene/types";
|
||||||
import { distance, getFontString, getFontFamilyString, isRTL } from "../utils";
|
import { distance, getFontString, getFontFamilyString, isRTL } from "../utils";
|
||||||
import { getCornerRadius, isPathALoop, isRightAngle } from "../math";
|
import { getCornerRadius, isPathALoop, isRightAngle } from "../math";
|
||||||
import rough from "roughjs/bin/rough";
|
import rough from "@excalidraw/roughjs/bin/rough";
|
||||||
import { AppState, BinaryFiles, Zoom } from "../types";
|
import { AppState, BinaryFiles, Zoom } from "../types";
|
||||||
import { getDefaultAppState } from "../appState";
|
import { getDefaultAppState } from "../appState";
|
||||||
import {
|
import {
|
||||||
@@ -87,12 +87,66 @@ export interface ExcalidrawElementWithCanvas {
|
|||||||
element: ExcalidrawElement | ExcalidrawTextElement;
|
element: ExcalidrawElement | ExcalidrawTextElement;
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
theme: RenderConfig["theme"];
|
theme: RenderConfig["theme"];
|
||||||
canvasZoom: Zoom["value"];
|
scale: number;
|
||||||
canvasOffsetX: number;
|
canvasOffsetX: number;
|
||||||
canvasOffsetY: number;
|
canvasOffsetY: number;
|
||||||
boundTextElementVersion: number | null;
|
boundTextElementVersion: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cappedElementCanvasSize = (
|
||||||
|
element: NonDeletedExcalidrawElement,
|
||||||
|
zoom: Zoom,
|
||||||
|
): {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
scale: number;
|
||||||
|
} => {
|
||||||
|
// these limits are ballpark, they depend on specific browsers and device.
|
||||||
|
// We've chosen lower limits to be safe. We might want to change these limits
|
||||||
|
// based on browser/device type, if we get reports of low quality rendering
|
||||||
|
// on zoom.
|
||||||
|
//
|
||||||
|
// ~ safari mobile canvas area limit
|
||||||
|
const AREA_LIMIT = 16777216;
|
||||||
|
// ~ safari width/height limit based on developer.mozilla.org.
|
||||||
|
const WIDTH_HEIGHT_LIMIT = 32767;
|
||||||
|
|
||||||
|
const padding = getCanvasPadding(element);
|
||||||
|
|
||||||
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||||
|
const elementWidth =
|
||||||
|
isLinearElement(element) || isFreeDrawElement(element)
|
||||||
|
? distance(x1, x2)
|
||||||
|
: element.width;
|
||||||
|
const elementHeight =
|
||||||
|
isLinearElement(element) || isFreeDrawElement(element)
|
||||||
|
? distance(y1, y2)
|
||||||
|
: element.height;
|
||||||
|
|
||||||
|
let width = elementWidth * window.devicePixelRatio + padding * 2;
|
||||||
|
let height = elementHeight * window.devicePixelRatio + padding * 2;
|
||||||
|
|
||||||
|
let scale: number = zoom.value;
|
||||||
|
|
||||||
|
// rescale to ensure width and height is within limits
|
||||||
|
if (
|
||||||
|
width * scale > WIDTH_HEIGHT_LIMIT ||
|
||||||
|
height * scale > WIDTH_HEIGHT_LIMIT
|
||||||
|
) {
|
||||||
|
scale = Math.min(WIDTH_HEIGHT_LIMIT / width, WIDTH_HEIGHT_LIMIT / height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rescale to ensure canvas area is within limits
|
||||||
|
if (width * height * scale * scale > AREA_LIMIT) {
|
||||||
|
scale = Math.sqrt(AREA_LIMIT / (width * height));
|
||||||
|
}
|
||||||
|
|
||||||
|
width = Math.floor(width * scale);
|
||||||
|
height = Math.floor(height * scale);
|
||||||
|
|
||||||
|
return { width, height, scale };
|
||||||
|
};
|
||||||
|
|
||||||
const generateElementCanvas = (
|
const generateElementCanvas = (
|
||||||
element: NonDeletedExcalidrawElement,
|
element: NonDeletedExcalidrawElement,
|
||||||
zoom: Zoom,
|
zoom: Zoom,
|
||||||
@@ -102,44 +156,35 @@ const generateElementCanvas = (
|
|||||||
const context = canvas.getContext("2d")!;
|
const context = canvas.getContext("2d")!;
|
||||||
const padding = getCanvasPadding(element);
|
const padding = getCanvasPadding(element);
|
||||||
|
|
||||||
|
const { width, height, scale } = cappedElementCanvasSize(element, zoom);
|
||||||
|
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
let canvasOffsetX = 0;
|
let canvasOffsetX = 0;
|
||||||
let canvasOffsetY = 0;
|
let canvasOffsetY = 0;
|
||||||
|
|
||||||
if (isLinearElement(element) || isFreeDrawElement(element)) {
|
if (isLinearElement(element) || isFreeDrawElement(element)) {
|
||||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
const [x1, y1] = getElementAbsoluteCoords(element);
|
||||||
|
|
||||||
canvas.width =
|
|
||||||
distance(x1, x2) * window.devicePixelRatio * zoom.value +
|
|
||||||
padding * zoom.value * 2;
|
|
||||||
canvas.height =
|
|
||||||
distance(y1, y2) * window.devicePixelRatio * zoom.value +
|
|
||||||
padding * zoom.value * 2;
|
|
||||||
|
|
||||||
canvasOffsetX =
|
canvasOffsetX =
|
||||||
element.x > x1
|
element.x > x1
|
||||||
? distance(element.x, x1) * window.devicePixelRatio * zoom.value
|
? distance(element.x, x1) * window.devicePixelRatio * scale
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
canvasOffsetY =
|
canvasOffsetY =
|
||||||
element.y > y1
|
element.y > y1
|
||||||
? distance(element.y, y1) * window.devicePixelRatio * zoom.value
|
? distance(element.y, y1) * window.devicePixelRatio * scale
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
context.translate(canvasOffsetX, canvasOffsetY);
|
context.translate(canvasOffsetX, canvasOffsetY);
|
||||||
} else {
|
|
||||||
canvas.width =
|
|
||||||
element.width * window.devicePixelRatio * zoom.value +
|
|
||||||
padding * zoom.value * 2;
|
|
||||||
canvas.height =
|
|
||||||
element.height * window.devicePixelRatio * zoom.value +
|
|
||||||
padding * zoom.value * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(padding * zoom.value, padding * zoom.value);
|
context.translate(padding * scale, padding * scale);
|
||||||
context.scale(
|
context.scale(
|
||||||
window.devicePixelRatio * zoom.value,
|
window.devicePixelRatio * scale,
|
||||||
window.devicePixelRatio * zoom.value,
|
window.devicePixelRatio * scale,
|
||||||
);
|
);
|
||||||
|
|
||||||
const rc = rough.canvas(canvas);
|
const rc = rough.canvas(canvas);
|
||||||
@@ -156,7 +201,7 @@ const generateElementCanvas = (
|
|||||||
element,
|
element,
|
||||||
canvas,
|
canvas,
|
||||||
theme: renderConfig.theme,
|
theme: renderConfig.theme,
|
||||||
canvasZoom: zoom.value,
|
scale,
|
||||||
canvasOffsetX,
|
canvasOffsetX,
|
||||||
canvasOffsetY,
|
canvasOffsetY,
|
||||||
boundTextElementVersion: getBoundTextElement(element)?.version || null,
|
boundTextElementVersion: getBoundTextElement(element)?.version || null,
|
||||||
@@ -344,6 +389,16 @@ export const setShapeForElement = <T extends ExcalidrawElement>(
|
|||||||
export const invalidateShapeForElement = (element: ExcalidrawElement) =>
|
export const invalidateShapeForElement = (element: ExcalidrawElement) =>
|
||||||
shapeCache.delete(element);
|
shapeCache.delete(element);
|
||||||
|
|
||||||
|
function adjustRoughness(size: number, roughness: number): number {
|
||||||
|
if (size >= 50) {
|
||||||
|
return roughness;
|
||||||
|
}
|
||||||
|
const factor = 2 + (50 - size) / 10;
|
||||||
|
|
||||||
|
// console.log({ ret: roughness / factor, roughness, factor, size });
|
||||||
|
return roughness / factor;
|
||||||
|
}
|
||||||
|
|
||||||
export const generateRoughOptions = (
|
export const generateRoughOptions = (
|
||||||
element: ExcalidrawElement,
|
element: ExcalidrawElement,
|
||||||
continuousPath = false,
|
continuousPath = false,
|
||||||
@@ -370,7 +425,10 @@ export const generateRoughOptions = (
|
|||||||
// calculate them (and we don't want the fills to be modified)
|
// calculate them (and we don't want the fills to be modified)
|
||||||
fillWeight: element.strokeWidth / 2,
|
fillWeight: element.strokeWidth / 2,
|
||||||
hachureGap: element.strokeWidth * 4,
|
hachureGap: element.strokeWidth * 4,
|
||||||
roughness: element.roughness,
|
roughness: adjustRoughness(
|
||||||
|
Math.min(element.width, element.height),
|
||||||
|
element.roughness,
|
||||||
|
),
|
||||||
stroke: element.strokeColor,
|
stroke: element.strokeColor,
|
||||||
preserveVertices: continuousPath,
|
preserveVertices: continuousPath,
|
||||||
};
|
};
|
||||||
@@ -670,7 +728,7 @@ const generateElementWithCanvas = (
|
|||||||
const prevElementWithCanvas = elementWithCanvasCache.get(element);
|
const prevElementWithCanvas = elementWithCanvasCache.get(element);
|
||||||
const shouldRegenerateBecauseZoom =
|
const shouldRegenerateBecauseZoom =
|
||||||
prevElementWithCanvas &&
|
prevElementWithCanvas &&
|
||||||
prevElementWithCanvas.canvasZoom !== zoom.value &&
|
prevElementWithCanvas.scale !== zoom.value &&
|
||||||
!renderConfig?.shouldCacheIgnoreZoom;
|
!renderConfig?.shouldCacheIgnoreZoom;
|
||||||
const boundTextElementVersion = getBoundTextElement(element)?.version || null;
|
const boundTextElementVersion = getBoundTextElement(element)?.version || null;
|
||||||
|
|
||||||
@@ -701,7 +759,7 @@ const drawElementFromCanvas = (
|
|||||||
) => {
|
) => {
|
||||||
const element = elementWithCanvas.element;
|
const element = elementWithCanvas.element;
|
||||||
const padding = getCanvasPadding(element);
|
const padding = getCanvasPadding(element);
|
||||||
const zoom = elementWithCanvas.canvasZoom;
|
const zoom = elementWithCanvas.scale;
|
||||||
let [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
let [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||||
|
|
||||||
// Free draw elements will otherwise "shuffle" as the min x and y change
|
// Free draw elements will otherwise "shuffle" as the min x and y change
|
||||||
@@ -728,10 +786,10 @@ const drawElementFromCanvas = (
|
|||||||
const maxDim = Math.max(distance(x1, x2), distance(y1, y2));
|
const maxDim = Math.max(distance(x1, x2), distance(y1, y2));
|
||||||
tempCanvas.width =
|
tempCanvas.width =
|
||||||
maxDim * window.devicePixelRatio * zoom +
|
maxDim * window.devicePixelRatio * zoom +
|
||||||
padding * elementWithCanvas.canvasZoom * 10;
|
padding * elementWithCanvas.scale * 10;
|
||||||
tempCanvas.height =
|
tempCanvas.height =
|
||||||
maxDim * window.devicePixelRatio * zoom +
|
maxDim * window.devicePixelRatio * zoom +
|
||||||
padding * elementWithCanvas.canvasZoom * 10;
|
padding * elementWithCanvas.scale * 10;
|
||||||
const offsetX = (tempCanvas.width - elementWithCanvas.canvas!.width) / 2;
|
const offsetX = (tempCanvas.width - elementWithCanvas.canvas!.width) / 2;
|
||||||
const offsetY = (tempCanvas.height - elementWithCanvas.canvas!.height) / 2;
|
const offsetY = (tempCanvas.height - elementWithCanvas.canvas!.height) / 2;
|
||||||
|
|
||||||
@@ -812,11 +870,11 @@ const drawElementFromCanvas = (
|
|||||||
context.drawImage(
|
context.drawImage(
|
||||||
elementWithCanvas.canvas!,
|
elementWithCanvas.canvas!,
|
||||||
(x1 + renderConfig.scrollX) * window.devicePixelRatio -
|
(x1 + renderConfig.scrollX) * window.devicePixelRatio -
|
||||||
(padding * elementWithCanvas.canvasZoom) / elementWithCanvas.canvasZoom,
|
(padding * elementWithCanvas.scale) / elementWithCanvas.scale,
|
||||||
(y1 + renderConfig.scrollY) * window.devicePixelRatio -
|
(y1 + renderConfig.scrollY) * window.devicePixelRatio -
|
||||||
(padding * elementWithCanvas.canvasZoom) / elementWithCanvas.canvasZoom,
|
(padding * elementWithCanvas.scale) / elementWithCanvas.scale,
|
||||||
elementWithCanvas.canvas!.width / elementWithCanvas.canvasZoom,
|
elementWithCanvas.canvas!.width / elementWithCanvas.scale,
|
||||||
elementWithCanvas.canvas!.height / elementWithCanvas.canvasZoom,
|
elementWithCanvas.canvas!.height / elementWithCanvas.scale,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "@excalidraw/roughjs/bin/canvas";
|
||||||
import { RoughSVG } from "roughjs/bin/svg";
|
import { RoughSVG } from "@excalidraw/roughjs/bin/svg";
|
||||||
import oc from "open-color";
|
import oc from "open-color";
|
||||||
|
|
||||||
import { AppState, BinaryFiles, Point, Zoom } from "../types";
|
import { AppState, BinaryFiles, Point, Zoom } from "../types";
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import rough from "roughjs/bin/rough";
|
import rough from "@excalidraw/roughjs/bin/rough";
|
||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import { getCommonBounds } from "../element/bounds";
|
import { getCommonBounds } from "../element/bounds";
|
||||||
import { renderScene, renderSceneToSvg } from "../renderer/renderScene";
|
import { renderScene, renderSceneToSvg } from "../renderer/renderScene";
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ import {
|
|||||||
StrokeRoundness,
|
StrokeRoundness,
|
||||||
} from "./element/types";
|
} from "./element/types";
|
||||||
import { SHAPES } from "./shapes";
|
import { SHAPES } from "./shapes";
|
||||||
import { Point as RoughPoint } from "roughjs/bin/geometry";
|
import { Point as RoughPoint } from "@excalidraw/roughjs/bin/geometry";
|
||||||
import { LinearElementEditor } from "./element/linearElementEditor";
|
import { LinearElementEditor } from "./element/linearElementEditor";
|
||||||
import { SuggestedBinding } from "./element/binding";
|
import { SuggestedBinding } from "./element/binding";
|
||||||
import { ImportedDataState } from "./data/types";
|
import { ImportedDataState } from "./data/types";
|
||||||
|
|||||||
@@ -1484,6 +1484,15 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@excalidraw/prettier-config/-/prettier-config-1.0.2.tgz#b7c061c99cee2f78b9ca470ea1fbd602683bba65"
|
resolved "https://registry.yarnpkg.com/@excalidraw/prettier-config/-/prettier-config-1.0.2.tgz#b7c061c99cee2f78b9ca470ea1fbd602683bba65"
|
||||||
integrity sha512-rFIq8+A8WvkEzBsF++Rw6gzxE+hU3ZNkdg8foI+Upz2y/rOC/gUpWJaggPbCkoH3nlREVU59axQjZ1+F6ePRGg==
|
integrity sha512-rFIq8+A8WvkEzBsF++Rw6gzxE+hU3ZNkdg8foI+Upz2y/rOC/gUpWJaggPbCkoH3nlREVU59axQjZ1+F6ePRGg==
|
||||||
|
|
||||||
|
"@excalidraw/roughjs@4.5.3":
|
||||||
|
version "4.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@excalidraw/roughjs/-/roughjs-4.5.3.tgz#7d91897bfd1aaeb19a48d5272bd4776cb19f015b"
|
||||||
|
integrity sha512-X9HPfNjPLNHt+gN7t1LN7nN4h4o8M2EWuN/Y4YgG0ODsDZn51gRBeIPuhSo2hsDgLeTQHnkrmpPTm4XeeQVtoA==
|
||||||
|
dependencies:
|
||||||
|
path-data-parser "^0.1.0"
|
||||||
|
points-on-curve "^0.2.0"
|
||||||
|
points-on-path "^0.2.1"
|
||||||
|
|
||||||
"@firebase/analytics-types@0.4.0":
|
"@firebase/analytics-types@0.4.0":
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.4.0.tgz#d6716f9fa36a6e340bc0ecfe68af325aa6f60508"
|
resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.4.0.tgz#d6716f9fa36a6e340bc0ecfe68af325aa6f60508"
|
||||||
|
|||||||
Reference in New Issue
Block a user