|
|
@ -134,8 +134,7 @@ function exportAsPNG({
|
|
|
|
// deselect & rerender
|
|
|
|
// deselect & rerender
|
|
|
|
|
|
|
|
|
|
|
|
clearSelection();
|
|
|
|
clearSelection();
|
|
|
|
drawScene();
|
|
|
|
ReactDOM.render(<App />, rootElement, () => {
|
|
|
|
|
|
|
|
|
|
|
|
// calculate visible-area coords
|
|
|
|
// calculate visible-area coords
|
|
|
|
|
|
|
|
|
|
|
|
let subCanvasX1 = Infinity;
|
|
|
|
let subCanvasX1 = Infinity;
|
|
|
@ -198,6 +197,7 @@ function exportAsPNG({
|
|
|
|
// clean up the DOM
|
|
|
|
// clean up the DOM
|
|
|
|
link.remove();
|
|
|
|
link.remove();
|
|
|
|
if (tempCanvas !== canvas) tempCanvas.remove();
|
|
|
|
if (tempCanvas !== canvas) tempCanvas.remove();
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function rotate(x1: number, y1: number, x2: number, y2: number, angle: number) {
|
|
|
|
function rotate(x1: number, y1: number, x2: number, y2: number, angle: number) {
|
|
|
@ -396,11 +396,11 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
|
|
|
|
|
|
|
|
if (event.key === "Escape") {
|
|
|
|
if (event.key === "Escape") {
|
|
|
|
clearSelection();
|
|
|
|
clearSelection();
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
event.preventDefault();
|
|
|
|
event.preventDefault();
|
|
|
|
} else if (event.key === "Backspace") {
|
|
|
|
} else if (event.key === "Backspace") {
|
|
|
|
deleteSelectedElements();
|
|
|
|
deleteSelectedElements();
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
event.preventDefault();
|
|
|
|
event.preventDefault();
|
|
|
|
} else if (
|
|
|
|
} else if (
|
|
|
|
event.key === "ArrowLeft" ||
|
|
|
|
event.key === "ArrowLeft" ||
|
|
|
@ -417,13 +417,13 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
else if (event.key === "ArrowDown") element.y += step;
|
|
|
|
else if (event.key === "ArrowDown") element.y += step;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
event.preventDefault();
|
|
|
|
event.preventDefault();
|
|
|
|
} else if (event.key === "a" && event.metaKey) {
|
|
|
|
} else if (event.key === "a" && event.metaKey) {
|
|
|
|
elements.forEach(element => {
|
|
|
|
elements.forEach(element => {
|
|
|
|
element.isSelected = true;
|
|
|
|
element.isSelected = true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
event.preventDefault();
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -443,7 +443,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
onChange={() => {
|
|
|
|
onChange={() => {
|
|
|
|
this.setState({ elementType: type });
|
|
|
|
this.setState({ elementType: type });
|
|
|
|
clearSelection();
|
|
|
|
clearSelection();
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
{children}
|
|
|
|
{children}
|
|
|
@ -453,15 +453,6 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
|
|
|
|
|
|
|
|
public render() {
|
|
|
|
public render() {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
|
|
|
|
<fieldset>
|
|
|
|
|
|
|
|
<legend>Shapes</legend>
|
|
|
|
|
|
|
|
{this.renderOption({ type: "rectangle", children: "Rectangle" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "ellipse", children: "Ellipse" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "arrow", children: "Arrow" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "text", children: "Text" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "selection", children: "Selection" })}
|
|
|
|
|
|
|
|
</fieldset>
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
onCut={e => {
|
|
|
|
onCut={e => {
|
|
|
|
e.clipboardData.setData(
|
|
|
|
e.clipboardData.setData(
|
|
|
@ -469,7 +460,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
JSON.stringify(elements.filter(element => element.isSelected))
|
|
|
|
JSON.stringify(elements.filter(element => element.isSelected))
|
|
|
|
);
|
|
|
|
);
|
|
|
|
deleteSelectedElements();
|
|
|
|
deleteSelectedElements();
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
e.preventDefault();
|
|
|
|
e.preventDefault();
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
onCopy={e => {
|
|
|
|
onCopy={e => {
|
|
|
@ -501,11 +492,20 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
elements.push(parsedElement);
|
|
|
|
elements.push(parsedElement);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
e.preventDefault();
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
<fieldset>
|
|
|
|
|
|
|
|
<legend>Shapes</legend>
|
|
|
|
|
|
|
|
{this.renderOption({ type: "rectangle", children: "Rectangle" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "ellipse", children: "Ellipse" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "arrow", children: "Arrow" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "text", children: "Text" })}
|
|
|
|
|
|
|
|
{this.renderOption({ type: "selection", children: "Selection" })}
|
|
|
|
|
|
|
|
</fieldset>
|
|
|
|
|
|
|
|
|
|
|
|
<canvas
|
|
|
|
<canvas
|
|
|
|
id="canvas"
|
|
|
|
id="canvas"
|
|
|
|
width={window.innerWidth}
|
|
|
|
width={window.innerWidth}
|
|
|
@ -539,9 +539,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
clearSelection();
|
|
|
|
clearSelection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isDraggingElements = elements.some(
|
|
|
|
isDraggingElements = elements.some(element => element.isSelected);
|
|
|
|
element => element.isSelected
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isDraggingElements) {
|
|
|
|
if (isDraggingElements) {
|
|
|
|
document.documentElement.style.cursor = "move";
|
|
|
|
document.documentElement.style.cursor = "move";
|
|
|
@ -564,8 +562,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
} = context.measureText(element.text);
|
|
|
|
} = context.measureText(element.text);
|
|
|
|
element.actualBoundingBoxAscent = actualBoundingBoxAscent;
|
|
|
|
element.actualBoundingBoxAscent = actualBoundingBoxAscent;
|
|
|
|
context.font = font;
|
|
|
|
context.font = font;
|
|
|
|
const height =
|
|
|
|
const height = actualBoundingBoxAscent + actualBoundingBoxDescent;
|
|
|
|
actualBoundingBoxAscent + actualBoundingBoxDescent;
|
|
|
|
|
|
|
|
// Center the text
|
|
|
|
// Center the text
|
|
|
|
element.x -= width / 2;
|
|
|
|
element.x -= width / 2;
|
|
|
|
element.y -= actualBoundingBoxAscent;
|
|
|
|
element.y -= actualBoundingBoxAscent;
|
|
|
@ -609,7 +606,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
lastX = x;
|
|
|
|
lastX = x;
|
|
|
|
lastY = y;
|
|
|
|
lastY = y;
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -633,7 +630,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
if (this.state.elementType === "selection") {
|
|
|
|
if (this.state.elementType === "selection") {
|
|
|
|
setSelection(draggingElement);
|
|
|
|
setSelection(draggingElement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const onMouseUp = (e: MouseEvent) => {
|
|
|
|
const onMouseUp = (e: MouseEvent) => {
|
|
|
@ -647,7 +644,7 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
// if no element is clicked, clear the selection and redraw
|
|
|
|
// if no element is clicked, clear the selection and redraw
|
|
|
|
if (draggingElement === null) {
|
|
|
|
if (draggingElement === null) {
|
|
|
|
clearSelection();
|
|
|
|
clearSelection();
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -664,16 +661,15 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
draggingElement: null,
|
|
|
|
draggingElement: null,
|
|
|
|
elementType: "selection"
|
|
|
|
elementType: "selection"
|
|
|
|
});
|
|
|
|
});
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener("mousemove", onMouseMove);
|
|
|
|
window.addEventListener("mousemove", onMouseMove);
|
|
|
|
window.addEventListener("mouseup", onMouseUp);
|
|
|
|
window.addEventListener("mouseup", onMouseUp);
|
|
|
|
|
|
|
|
|
|
|
|
drawScene();
|
|
|
|
this.forceUpdate();
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<fieldset>
|
|
|
|
<fieldset>
|
|
|
|
<legend>Colors</legend>
|
|
|
|
<legend>Colors</legend>
|
|
|
|
<label>
|
|
|
|
<label>
|
|
|
@ -752,9 +748,10 @@ class App extends React.Component<{}, AppState> {
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
px)
|
|
|
|
px)
|
|
|
|
</fieldset>
|
|
|
|
</fieldset>
|
|
|
|
</>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
componentDidUpdate() {
|
|
|
|
componentDidUpdate() {
|
|
|
|
const fillStyle = context.fillStyle;
|
|
|
|
const fillStyle = context.fillStyle;
|
|
|
|
context.fillStyle = this.state.viewBgColor;
|
|
|
|
context.fillStyle = this.state.viewBgColor;
|
|
|
@ -794,8 +791,4 @@ const context = canvas.getContext("2d")!;
|
|
|
|
// https://stackoverflow.com/questions/13879322/drawing-a-1px-thick-line-in-canvas-creates-a-2px-thick-line/13879402#comment90766599_13879402
|
|
|
|
// https://stackoverflow.com/questions/13879322/drawing-a-1px-thick-line-in-canvas-creates-a-2px-thick-line/13879402#comment90766599_13879402
|
|
|
|
context.translate(0.5, 0.5);
|
|
|
|
context.translate(0.5, 0.5);
|
|
|
|
|
|
|
|
|
|
|
|
function drawScene() {
|
|
|
|
ReactDOM.render(<App />, rootElement);
|
|
|
|
ReactDOM.render(<App />, rootElement);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
drawScene();
|
|
|
|
|
|
|
|