Regenerate roughjs shape only when the item is updated (#316)
* Regenerate roughjs shape only when the item is updated * Remove shape object during export and history undo/redo * Remove shape element during copying * Fix shape generation during creation
This commit is contained in:
parent
1bf18fe4ed
commit
74764b06eb
@ -24,6 +24,7 @@ export const actionChangeStrokeColor: Action = {
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, el => ({
|
elements: changeProperty(elements, el => ({
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
strokeColor: value
|
strokeColor: value
|
||||||
})),
|
})),
|
||||||
appState: { ...appState, currentItemStrokeColor: value }
|
appState: { ...appState, currentItemStrokeColor: value }
|
||||||
@ -50,6 +51,7 @@ export const actionChangeBackgroundColor: Action = {
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, el => ({
|
elements: changeProperty(elements, el => ({
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
backgroundColor: value
|
backgroundColor: value
|
||||||
})),
|
})),
|
||||||
appState: { ...appState, currentItemBackgroundColor: value }
|
appState: { ...appState, currentItemBackgroundColor: value }
|
||||||
@ -76,6 +78,7 @@ export const actionChangeFillStyle: Action = {
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, el => ({
|
elements: changeProperty(elements, el => ({
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
fillStyle: value
|
fillStyle: value
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
@ -104,6 +107,7 @@ export const actionChangeStrokeWidth: Action = {
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, el => ({
|
elements: changeProperty(elements, el => ({
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
strokeWidth: value
|
strokeWidth: value
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
@ -130,6 +134,7 @@ export const actionChangeSloppiness: Action = {
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, el => ({
|
elements: changeProperty(elements, el => ({
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
roughness: value
|
roughness: value
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
@ -156,6 +161,7 @@ export const actionChangeOpacity: Action = {
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, el => ({
|
elements: changeProperty(elements, el => ({
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
opacity: value
|
opacity: value
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
@ -185,6 +191,7 @@ export const actionChangeFontSize: Action = {
|
|||||||
if (isTextElement(el)) {
|
if (isTextElement(el)) {
|
||||||
const element: ExcalidrawTextElement = {
|
const element: ExcalidrawTextElement = {
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
font: `${value}px ${el.font.split("px ")[1]}`
|
font: `${value}px ${el.font.split("px ")[1]}`
|
||||||
};
|
};
|
||||||
redrawTextBoundingBox(element);
|
redrawTextBoundingBox(element);
|
||||||
@ -223,6 +230,7 @@ export const actionChangeFontFamily: Action = {
|
|||||||
if (isTextElement(el)) {
|
if (isTextElement(el)) {
|
||||||
const element: ExcalidrawTextElement = {
|
const element: ExcalidrawTextElement = {
|
||||||
...el,
|
...el,
|
||||||
|
shape: null,
|
||||||
font: `${el.font.split("px ")[0]}px ${value}`
|
font: `${el.font.split("px ")[0]}px ${value}`
|
||||||
};
|
};
|
||||||
redrawTextBoundingBox(element);
|
redrawTextBoundingBox(element);
|
||||||
|
@ -27,6 +27,7 @@ export const actionPasteStyles: Action = {
|
|||||||
if (element.isSelected) {
|
if (element.isSelected) {
|
||||||
const newElement = {
|
const newElement = {
|
||||||
...element,
|
...element,
|
||||||
|
shape: null,
|
||||||
backgroundColor: pastedElement?.backgroundColor,
|
backgroundColor: pastedElement?.backgroundColor,
|
||||||
strokeWidth: pastedElement?.strokeWidth,
|
strokeWidth: pastedElement?.strokeWidth,
|
||||||
strokeColor: pastedElement?.strokeColor,
|
strokeColor: pastedElement?.strokeColor,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { randomSeed } from "../random";
|
import { randomSeed } from "../random";
|
||||||
import nanoid from "nanoid";
|
import nanoid from "nanoid";
|
||||||
|
import { Drawable } from "roughjs/bin/core";
|
||||||
|
|
||||||
export function newElement(
|
export function newElement(
|
||||||
type: string,
|
type: string,
|
||||||
@ -28,7 +29,8 @@ export function newElement(
|
|||||||
roughness,
|
roughness,
|
||||||
opacity,
|
opacity,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
seed: randomSeed()
|
seed: randomSeed(),
|
||||||
|
shape: null as Drawable | Drawable[] | null
|
||||||
};
|
};
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ class SceneHistory {
|
|||||||
|
|
||||||
generateCurrentEntry(elements: readonly ExcalidrawElement[]) {
|
generateCurrentEntry(elements: readonly ExcalidrawElement[]) {
|
||||||
return JSON.stringify(
|
return JSON.stringify(
|
||||||
elements.map(element => ({ ...element, isSelected: false }))
|
elements.map(({ shape, ...element }) => ({
|
||||||
|
...element,
|
||||||
|
isSelected: false
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,9 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
private copyToClipboard = () => {
|
private copyToClipboard = () => {
|
||||||
if (navigator.clipboard) {
|
if (navigator.clipboard) {
|
||||||
const text = JSON.stringify(
|
const text = JSON.stringify(
|
||||||
elements.filter(element => element.isSelected)
|
elements
|
||||||
|
.filter(element => element.isSelected)
|
||||||
|
.map(({ shape, ...el }) => el)
|
||||||
);
|
);
|
||||||
navigator.clipboard.writeText(text);
|
navigator.clipboard.writeText(text);
|
||||||
}
|
}
|
||||||
@ -303,7 +305,11 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
onCut={e => {
|
onCut={e => {
|
||||||
e.clipboardData.setData(
|
e.clipboardData.setData(
|
||||||
"text/plain",
|
"text/plain",
|
||||||
JSON.stringify(elements.filter(element => element.isSelected))
|
JSON.stringify(
|
||||||
|
elements
|
||||||
|
.filter(element => element.isSelected)
|
||||||
|
.map(({ shape, ...el }) => el)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
elements = deleteSelectedElements(elements);
|
elements = deleteSelectedElements(elements);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
@ -312,7 +318,11 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
onCopy={e => {
|
onCopy={e => {
|
||||||
e.clipboardData.setData(
|
e.clipboardData.setData(
|
||||||
"text/plain",
|
"text/plain",
|
||||||
JSON.stringify(elements.filter(element => element.isSelected))
|
JSON.stringify(
|
||||||
|
elements
|
||||||
|
.filter(element => element.isSelected)
|
||||||
|
.map(({ shape, ...el }) => el)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
@ -465,6 +475,7 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
1,
|
1,
|
||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
|
||||||
type ResizeTestType = ReturnType<typeof resizeTest>;
|
type ResizeTestType = ReturnType<typeof resizeTest>;
|
||||||
let resizeHandle: ResizeTestType = false;
|
let resizeHandle: ResizeTestType = false;
|
||||||
let isResizingElements = false;
|
let isResizingElements = false;
|
||||||
@ -670,6 +681,7 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
|
|
||||||
el.x = element.x;
|
el.x = element.x;
|
||||||
el.y = element.y;
|
el.y = element.y;
|
||||||
|
el.shape = null;
|
||||||
});
|
});
|
||||||
lastX = x;
|
lastX = x;
|
||||||
lastY = y;
|
lastY = y;
|
||||||
@ -705,6 +717,7 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
// otherwise we would read a stale one!
|
// otherwise we would read a stale one!
|
||||||
const draggingElement = this.state.draggingElement;
|
const draggingElement = this.state.draggingElement;
|
||||||
if (!draggingElement) return;
|
if (!draggingElement) return;
|
||||||
|
|
||||||
let width =
|
let width =
|
||||||
e.clientX -
|
e.clientX -
|
||||||
CANVAS_WINDOW_OFFSET_LEFT -
|
CANVAS_WINDOW_OFFSET_LEFT -
|
||||||
@ -720,6 +733,7 @@ export class App extends React.Component<{}, AppState> {
|
|||||||
draggingElement.height = e.shiftKey
|
draggingElement.height = e.shiftKey
|
||||||
? Math.abs(width) * Math.sign(height)
|
? Math.abs(width) * Math.sign(height)
|
||||||
: height;
|
: height;
|
||||||
|
draggingElement.shape = null;
|
||||||
|
|
||||||
if (this.state.elementType === "selection") {
|
if (this.state.elementType === "selection") {
|
||||||
elements = setSelection(elements, draggingElement);
|
elements = setSelection(elements, draggingElement);
|
||||||
|
@ -4,6 +4,7 @@ import { ExcalidrawElement } from "../element/types";
|
|||||||
import { isTextElement } from "../element/typeChecks";
|
import { isTextElement } from "../element/typeChecks";
|
||||||
import { getDiamondPoints, getArrowPoints } from "../element/bounds";
|
import { getDiamondPoints, getArrowPoints } from "../element/bounds";
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
|
import { Drawable } from "roughjs/bin/core";
|
||||||
|
|
||||||
export function renderElement(
|
export function renderElement(
|
||||||
element: ExcalidrawElement,
|
element: ExcalidrawElement,
|
||||||
@ -17,69 +18,76 @@ export function renderElement(
|
|||||||
context.fillRect(0, 0, element.width, element.height);
|
context.fillRect(0, 0, element.width, element.height);
|
||||||
context.fillStyle = fillStyle;
|
context.fillStyle = fillStyle;
|
||||||
} else if (element.type === "rectangle") {
|
} else if (element.type === "rectangle") {
|
||||||
const shape = withCustomMathRandom(element.seed, () => {
|
if (!element.shape) {
|
||||||
return generator.rectangle(0, 0, element.width, element.height, {
|
element.shape = withCustomMathRandom(element.seed, () => {
|
||||||
stroke: element.strokeColor,
|
return generator.rectangle(0, 0, element.width, element.height, {
|
||||||
fill: element.backgroundColor,
|
stroke: element.strokeColor,
|
||||||
fillStyle: element.fillStyle,
|
fill: element.backgroundColor,
|
||||||
strokeWidth: element.strokeWidth,
|
fillStyle: element.fillStyle,
|
||||||
roughness: element.roughness
|
strokeWidth: element.strokeWidth,
|
||||||
|
roughness: element.roughness
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
context.globalAlpha = element.opacity / 100;
|
context.globalAlpha = element.opacity / 100;
|
||||||
rc.draw(shape);
|
rc.draw(element.shape as Drawable);
|
||||||
context.globalAlpha = 1;
|
context.globalAlpha = 1;
|
||||||
} else if (element.type === "diamond") {
|
} else if (element.type === "diamond") {
|
||||||
const shape = withCustomMathRandom(element.seed, () => {
|
if (!element.shape) {
|
||||||
const [
|
element.shape = withCustomMathRandom(element.seed, () => {
|
||||||
topX,
|
const [
|
||||||
topY,
|
topX,
|
||||||
rightX,
|
topY,
|
||||||
rightY,
|
rightX,
|
||||||
bottomX,
|
rightY,
|
||||||
bottomY,
|
bottomX,
|
||||||
leftX,
|
bottomY,
|
||||||
leftY
|
leftX,
|
||||||
] = getDiamondPoints(element);
|
leftY
|
||||||
return generator.polygon(
|
] = getDiamondPoints(element);
|
||||||
[
|
return generator.polygon(
|
||||||
[topX, topY],
|
[
|
||||||
[rightX, rightY],
|
[topX, topY],
|
||||||
[bottomX, bottomY],
|
[rightX, rightY],
|
||||||
[leftX, leftY]
|
[bottomX, bottomY],
|
||||||
],
|
[leftX, leftY]
|
||||||
{
|
],
|
||||||
stroke: element.strokeColor,
|
{
|
||||||
fill: element.backgroundColor,
|
stroke: element.strokeColor,
|
||||||
fillStyle: element.fillStyle,
|
fill: element.backgroundColor,
|
||||||
strokeWidth: element.strokeWidth,
|
fillStyle: element.fillStyle,
|
||||||
roughness: element.roughness
|
strokeWidth: element.strokeWidth,
|
||||||
}
|
roughness: element.roughness
|
||||||
);
|
}
|
||||||
});
|
);
|
||||||
context.globalAlpha = element.opacity / 100;
|
});
|
||||||
rc.draw(shape);
|
}
|
||||||
context.globalAlpha = 1;
|
|
||||||
} else if (element.type === "ellipse") {
|
|
||||||
const shape = withCustomMathRandom(element.seed, () =>
|
|
||||||
generator.ellipse(
|
|
||||||
element.width / 2,
|
|
||||||
element.height / 2,
|
|
||||||
element.width,
|
|
||||||
element.height,
|
|
||||||
{
|
|
||||||
stroke: element.strokeColor,
|
|
||||||
fill: element.backgroundColor,
|
|
||||||
fillStyle: element.fillStyle,
|
|
||||||
strokeWidth: element.strokeWidth,
|
|
||||||
roughness: element.roughness
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
context.globalAlpha = element.opacity / 100;
|
context.globalAlpha = element.opacity / 100;
|
||||||
rc.draw(shape);
|
rc.draw(element.shape as Drawable);
|
||||||
|
context.globalAlpha = 1;
|
||||||
|
} else if (element.type === "ellipse") {
|
||||||
|
if (!element.shape) {
|
||||||
|
element.shape = withCustomMathRandom(element.seed, () =>
|
||||||
|
generator.ellipse(
|
||||||
|
element.width / 2,
|
||||||
|
element.height / 2,
|
||||||
|
element.width,
|
||||||
|
element.height,
|
||||||
|
{
|
||||||
|
stroke: element.strokeColor,
|
||||||
|
fill: element.backgroundColor,
|
||||||
|
fillStyle: element.fillStyle,
|
||||||
|
strokeWidth: element.strokeWidth,
|
||||||
|
roughness: element.roughness
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.globalAlpha = element.opacity / 100;
|
||||||
|
rc.draw(element.shape as Drawable);
|
||||||
context.globalAlpha = 1;
|
context.globalAlpha = 1;
|
||||||
} else if (element.type === "arrow") {
|
} else if (element.type === "arrow") {
|
||||||
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
||||||
@ -89,17 +97,19 @@ export function renderElement(
|
|||||||
roughness: element.roughness
|
roughness: element.roughness
|
||||||
};
|
};
|
||||||
|
|
||||||
const shapes = withCustomMathRandom(element.seed, () => [
|
if (!element.shape) {
|
||||||
// \
|
element.shape = withCustomMathRandom(element.seed, () => [
|
||||||
generator.line(x3, y3, x2, y2, options),
|
// \
|
||||||
// -----
|
generator.line(x3, y3, x2, y2, options),
|
||||||
generator.line(x1, y1, x2, y2, options),
|
// -----
|
||||||
// /
|
generator.line(x1, y1, x2, y2, options),
|
||||||
generator.line(x4, y4, x2, y2, options)
|
// /
|
||||||
]);
|
generator.line(x4, y4, x2, y2, options)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
context.globalAlpha = element.opacity / 100;
|
context.globalAlpha = element.opacity / 100;
|
||||||
shapes.forEach(shape => rc.draw(shape));
|
(element.shape as Drawable[]).forEach(shape => rc.draw(shape));
|
||||||
context.globalAlpha = 1;
|
context.globalAlpha = 1;
|
||||||
return;
|
return;
|
||||||
} else if (isTextElement(element)) {
|
} else if (isTextElement(element)) {
|
||||||
|
@ -35,7 +35,7 @@ export function saveAsJSON(
|
|||||||
const serialized = JSON.stringify({
|
const serialized = JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
source: window.location.origin,
|
source: window.location.origin,
|
||||||
elements
|
elements: elements.map(({ shape, ...el }) => el)
|
||||||
});
|
});
|
||||||
|
|
||||||
saveFile(
|
saveFile(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user