|
@@ -15,7 +15,6 @@ function newElement(type, x, y) {
|
|
|
height: 0,
|
|
|
isSelected: false
|
|
|
};
|
|
|
- generateShape(element);
|
|
|
return element;
|
|
|
}
|
|
|
|
|
@@ -34,8 +33,10 @@ var generator = rough.generator();
|
|
|
function generateShape(element) {
|
|
|
if (element.type === "selection") {
|
|
|
element.draw = (rc, context) => {
|
|
|
+ const fillStyle = context.fillStyle;
|
|
|
context.fillStyle = "rgba(0, 0, 255, 0.10)";
|
|
|
context.fillRect(element.x, element.y, element.width, element.height);
|
|
|
+ context.fillStyle = fillStyle;
|
|
|
};
|
|
|
} else if (element.type === "rectangle") {
|
|
|
const shape = generator.rectangle(
|
|
@@ -85,29 +86,37 @@ function generateShape(element) {
|
|
|
};
|
|
|
return;
|
|
|
} else if (element.type === "text") {
|
|
|
- if (element.text === undefined) {
|
|
|
- element.text = prompt("What text do you want?");
|
|
|
- }
|
|
|
element.draw = (rc, context) => {
|
|
|
- context.font = "20px Virgil";
|
|
|
- const measure = context.measureText(element.text);
|
|
|
+ const font = context.font;
|
|
|
+ context.font = element.font;
|
|
|
const height =
|
|
|
- measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent;
|
|
|
+ element.measure.actualBoundingBoxAscent +
|
|
|
+ element.measure.actualBoundingBoxDescent;
|
|
|
context.fillText(
|
|
|
element.text,
|
|
|
- element.x - measure.width / 2,
|
|
|
- element.y + measure.actualBoundingBoxAscent - height / 2
|
|
|
+ element.x,
|
|
|
+ element.y + 2 * element.measure.actualBoundingBoxAscent - height / 2
|
|
|
);
|
|
|
+ context.font = font;
|
|
|
};
|
|
|
} else {
|
|
|
throw new Error("Unimplemented type " + element.type);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+function setSelection(selection) {
|
|
|
+ elements.forEach(element => {
|
|
|
+ element.isSelected =
|
|
|
+ selection.x < element.x &&
|
|
|
+ selection.y < element.y &&
|
|
|
+ selection.x + selection.width > element.x + element.width &&
|
|
|
+ selection.y + selection.height > element.y + element.height;
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
function App() {
|
|
|
const [draggingElement, setDraggingElement] = React.useState(null);
|
|
|
const [elementType, setElementType] = React.useState("selection");
|
|
|
- const [selectedElements, setSelectedElements] = React.useState([]);
|
|
|
function ElementOption({ type, children }) {
|
|
|
return (
|
|
|
<label>
|
|
@@ -132,21 +141,42 @@ function App() {
|
|
|
width={window.innerWidth}
|
|
|
height={window.innerHeight}
|
|
|
onMouseDown={e => {
|
|
|
- const element = newElement(
|
|
|
- elementType,
|
|
|
- e.clientX - e.target.offsetLeft,
|
|
|
- e.clientY - e.target.offsetTop
|
|
|
- );
|
|
|
+ const x = e.clientX - e.target.offsetLeft;
|
|
|
+ const y = e.clientY - e.target.offsetTop;
|
|
|
+ const element = newElement(elementType, x, y);
|
|
|
+
|
|
|
+ if (elementType === "text") {
|
|
|
+ element.text = prompt("What text do you want?");
|
|
|
+ element.font = "20px Virgil";
|
|
|
+ const font = context.font;
|
|
|
+ context.font = element.font;
|
|
|
+ element.measure = context.measureText(element.text);
|
|
|
+ context.font = font;
|
|
|
+ const height =
|
|
|
+ element.measure.actualBoundingBoxAscent +
|
|
|
+ element.measure.actualBoundingBoxDescent;
|
|
|
+ // Center the text
|
|
|
+ element.x -= element.measure.width / 2;
|
|
|
+ element.y -= element.measure.actualBoundingBoxAscent;
|
|
|
+ element.width = element.measure.width;
|
|
|
+ element.height = height;
|
|
|
+ }
|
|
|
+
|
|
|
+ generateShape(element);
|
|
|
elements.push(element);
|
|
|
- setDraggingElement(element);
|
|
|
+ if (elementType === "text") {
|
|
|
+ setDraggingElement(null);
|
|
|
+ } else {
|
|
|
+ setDraggingElement(element);
|
|
|
+ }
|
|
|
drawScene();
|
|
|
}}
|
|
|
onMouseUp={e => {
|
|
|
setDraggingElement(null);
|
|
|
if (elementType === "selection") {
|
|
|
- elements.forEach(element => {
|
|
|
- element.isSelected = false;
|
|
|
- });
|
|
|
+ // Remove actual selection element
|
|
|
+ elements.pop();
|
|
|
+ setSelection(draggingElement);
|
|
|
}
|
|
|
drawScene();
|
|
|
}}
|
|
@@ -157,18 +187,11 @@ function App() {
|
|
|
draggingElement.width = width;
|
|
|
// Make a perfect square or circle when shift is enabled
|
|
|
draggingElement.height = e.shiftKey ? width : height;
|
|
|
+
|
|
|
generateShape(draggingElement);
|
|
|
|
|
|
if (elementType === "selection") {
|
|
|
- elements.forEach(element => {
|
|
|
- element.isSelected =
|
|
|
- draggingElement.x <= element.x &&
|
|
|
- draggingElement.y <= element.y &&
|
|
|
- draggingElement.x + draggingElement.width >=
|
|
|
- element.x + element.width &&
|
|
|
- draggingElement.y + draggingElement.height >=
|
|
|
- element.y + element.height;
|
|
|
- });
|
|
|
+ setSelection(draggingElement);
|
|
|
}
|
|
|
drawScene();
|
|
|
}}
|
|
@@ -177,19 +200,21 @@ function App() {
|
|
|
);
|
|
|
}
|
|
|
const rootElement = document.getElementById("root");
|
|
|
+ReactDOM.render(<App />, rootElement);
|
|
|
+const canvas = document.getElementById("canvas");
|
|
|
+const rc = rough.canvas(canvas);
|
|
|
+const context = canvas.getContext("2d");
|
|
|
|
|
|
function drawScene() {
|
|
|
ReactDOM.render(<App />, rootElement);
|
|
|
|
|
|
- const canvas = document.getElementById("canvas");
|
|
|
- const rc = rough.canvas(canvas);
|
|
|
- const context = canvas.getContext("2d");
|
|
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
elements.forEach(element => {
|
|
|
element.draw(rc, context);
|
|
|
if (element.isSelected) {
|
|
|
const margin = 4;
|
|
|
+ const lineDash = context.getLineDash();
|
|
|
context.setLineDash([8, 4]);
|
|
|
context.strokeRect(
|
|
|
element.x - margin,
|
|
@@ -197,7 +222,7 @@ function drawScene() {
|
|
|
element.width + margin * 2,
|
|
|
element.height + margin * 2
|
|
|
);
|
|
|
- context.setLineDash([]);
|
|
|
+ context.setLineDash(lineDash);
|
|
|
}
|
|
|
});
|
|
|
}
|