Bladeren bron

Mouse move tracked outside window!

Christopher Chedeau 5 jaren geleden
bovenliggende
commit
457800caa3
1 gewijzigde bestanden met toevoegingen van 152 en 131 verwijderingen
  1. 152 131
      src/index.js

+ 152 - 131
src/index.js

@@ -150,148 +150,169 @@ function clearSelection() {
   });
 }
 
-function App() {
-  const [draggingElement, setDraggingElement] = React.useState(null);
-  const [elementType, setElementType] = React.useState("selection");
-  const onKeyDown = React.useCallback(event => {
-    if (event.key === "Backspace") {
-      for (var i = elements.length - 1; i >= 0; --i) {
-        if (elements[i].isSelected) {
-          elements.splice(i, 1);
+class App extends React.Component {
+  componentDidMount() {
+    this.onKeyDown = event => {
+      if (event.key === "Backspace") {
+        for (var i = elements.length - 1; i >= 0; --i) {
+          if (elements[i].isSelected) {
+            elements.splice(i, 1);
+          }
         }
+        drawScene();
+        event.preventDefault();
+      } else if (
+        event.key === "ArrowLeft" ||
+        event.key === "ArrowRight" ||
+        event.key === "ArrowUp" ||
+        event.key === "ArrowDown"
+      ) {
+        const step = event.shiftKey ? 5 : 1;
+        elements.forEach(element => {
+          if (element.isSelected) {
+            if (event.key === "ArrowLeft") element.x -= step;
+            else if (event.key === "ArrowRight") element.x += step;
+            else if (event.key === "ArrowUp") element.y -= step;
+            else if (event.key === "ArrowDown") element.y += step;
+          }
+        });
+        drawScene();
+        event.preventDefault();
       }
-      drawScene();
-      event.preventDefault();
-    } else if (
-      event.key === "ArrowLeft" ||
-      event.key === "ArrowRight" ||
-      event.key === "ArrowUp" ||
-      event.key === "ArrowDown"
-    ) {
-      const step = event.shiftKey ? 5 : 1;
-      elements.forEach(element => {
-        if (element.isSelected) {
-          if (event.key === "ArrowLeft") element.x -= step;
-          else if (event.key === "ArrowRight") element.x += step;
-          else if (event.key === "ArrowUp") element.y -= step;
-          else if (event.key === "ArrowDown") element.y += step;
-        }
-      });
-      drawScene();
-      event.preventDefault();
-    }
-  }, []);
-  React.useEffect(() => {
-    document.addEventListener("keydown", onKeyDown, false);
-    return () => {
-      document.removeEventListener("keydown", onKeyDown, false);
     };
-  }, [onKeyDown]);
+    document.addEventListener("keydown", this.onKeyDown, false);
+  }
 
-  function ElementOption({ type, children }) {
-    return (
-      <label>
-        <input
-          type="radio"
-          checked={elementType === type}
-          onChange={() => {
-            setElementType(type);
-            clearSelection();
-            drawScene();
-          }}
-        />
-        {children}
-      </label>
-    );
+  componentWillUnmount() {
+    document.removeEventListener("keydown", this.onKeyDown, false);
   }
-  return (
-    <div>
-      {/* Can't use the <ElementOption> form because ElementOption is re-defined
+
+  constructor() {
+    super();
+    this.state = {
+      draggingElement: null,
+      elementType: "selection"
+    };
+  }
+
+  render() {
+    const ElementOption = ({ type, children }) => {
+      return (
+        <label>
+          <input
+            type="radio"
+            checked={this.state.elementType === type}
+            onChange={() => {
+              this.setState({ elementType: type });
+              clearSelection();
+              drawScene();
+            }}
+          />
+          {children}
+        </label>
+      );
+    };
+
+    return (
+      <div>
+        {/* Can't use the <ElementOption> form because ElementOption is re-defined
           on every render, which would blow up and re-create the entire DOM tree,
           which in addition to being inneficient, messes up with browser text
           selection */}
-      {ElementOption({ type: "rectangle", children: "Rectangle" })}
-      {ElementOption({ type: "ellipse", children: "Ellipse" })}
-      {ElementOption({ type: "arrow", children: "Arrow" })}
-      {ElementOption({ type: "text", children: "Text" })}
-      {ElementOption({ type: "selection", children: "Selection" })}
-      <canvas
-        id="canvas"
-        width={window.innerWidth}
-        height={window.innerHeight}
-        onClick={e => {
-          console.log("click");
-        }}
-        onMouseDown={e => {
-          const x = e.clientX - e.target.offsetLeft;
-          const y = e.clientY - e.target.offsetTop;
-          const element = newElement(elementType, x, y);
-
-          if (elementType === "text") {
-            const text = prompt("What text do you want?");
-            if (text === null) {
-              return;
+        {ElementOption({ type: "rectangle", children: "Rectangle" })}
+        {ElementOption({ type: "ellipse", children: "Ellipse" })}
+        {ElementOption({ type: "arrow", children: "Arrow" })}
+        {ElementOption({ type: "text", children: "Text" })}
+        {ElementOption({ type: "selection", children: "Selection" })}
+        <canvas
+          id="canvas"
+          width={window.innerWidth}
+          height={window.innerHeight}
+          onMouseDown={e => {
+            const x = e.clientX - e.target.offsetLeft;
+            const y = e.clientY - e.target.offsetTop;
+            const element = newElement(this.state.elementType, x, y);
+
+            if (this.state.elementType === "text") {
+              const text = prompt("What text do you want?");
+              if (text === null) {
+                return;
+              }
+              element.text = text;
+              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;
             }
-            element.text = text;
-            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;
-          }
 
-          generateDraw(element);
-          elements.push(element);
-          if (elementType === "text") {
-            setDraggingElement(null);
-            element.isSelected = true;
-          } else {
-            setDraggingElement(element);
-          }
-          drawScene();
-        }}
-        onMouseUp={e => {
-          if (draggingElement === null) {
-            return;
-          }
-          if (elementType === "selection") {
-            // Remove actual selection element
-            elements.pop();
-            setSelection(draggingElement);
-          } else {
-            draggingElement.isSelected = true;
-          }
-          setDraggingElement(null);
-          setElementType("selection");
-          drawScene();
-        }}
-        onMouseMove={e => {
-          if (!draggingElement) return;
-          let width = e.clientX - e.target.offsetLeft - draggingElement.x;
-          let height = e.clientY - e.target.offsetTop - draggingElement.y;
-          draggingElement.width = width;
-          // Make a perfect square or circle when shift is enabled
-          draggingElement.height = e.shiftKey ? width : height;
-
-          generateDraw(draggingElement);
-
-          if (elementType === "selection") {
-            setSelection(draggingElement);
-          }
-          drawScene();
-        }}
-      />
-    </div>
-  );
+            generateDraw(element);
+            elements.push(element);
+            if (this.state.elementType === "text") {
+              this.setState({ draggingElement: null });
+              element.isSelected = true;
+            } else {
+              this.setState({ draggingElement: element });
+            }
+
+            const onMouseMove = e => {
+              // It is very important to read this.state within each move event,
+              // otherwise we would read a stale one!
+              const draggingElement = this.state.draggingElement;
+              if (!draggingElement) return;
+              let width = e.clientX - e.target.offsetLeft - draggingElement.x;
+              let height = e.clientY - e.target.offsetTop - draggingElement.y;
+              draggingElement.width = width;
+              // Make a perfect square or circle when shift is enabled
+              draggingElement.height = e.shiftKey ? width : height;
+
+              generateDraw(draggingElement);
+
+              if (this.state.elementType === "selection") {
+                setSelection(draggingElement);
+              }
+              drawScene();
+            };
+
+            const onMouseUp = e => {
+              window.removeEventListener("mousemove", onMouseMove);
+              window.removeEventListener("mouseup", onMouseUp);
+
+              const draggingElement = this.state.draggingElement;
+              if (draggingElement === null) {
+                return;
+              }
+              if (this.state.elementType === "selection") {
+                // Remove actual selection element
+                elements.pop();
+                setSelection(draggingElement);
+              } else {
+                draggingElement.isSelected = true;
+              }
+              this.setState({ draggingElement: null });
+              this.setState({ elementType: "selection" });
+              drawScene();
+            };
+
+            window.addEventListener("mousemove", onMouseMove);
+            window.addEventListener("mouseup", onMouseUp);
+
+            drawScene();
+          }}
+        />
+      </div>
+    );
+  }
 }
+
 const rootElement = document.getElementById("root");
 ReactDOM.render(<App />, rootElement);
 const canvas = document.getElementById("canvas");