Mouse move tracked outside window!

This commit is contained in:
Christopher Chedeau 2020-01-01 21:46:42 -08:00
parent 9aaaa24426
commit 457800caa3

View File

@ -150,148 +150,169 @@ function clearSelection() {
}); });
} }
function App() { class App extends React.Component {
const [draggingElement, setDraggingElement] = React.useState(null); componentDidMount() {
const [elementType, setElementType] = React.useState("selection"); this.onKeyDown = event => {
const onKeyDown = React.useCallback(event => { if (event.key === "Backspace") {
if (event.key === "Backspace") { for (var i = elements.length - 1; i >= 0; --i) {
for (var i = elements.length - 1; i >= 0; --i) { if (elements[i].isSelected) {
if (elements[i].isSelected) { elements.splice(i, 1);
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>
);
} }
return (
<div> componentWillUnmount() {
{/* Can't use the <ElementOption> form because ElementOption is re-defined document.removeEventListener("keydown", this.onKeyDown, false);
}
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, 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 which in addition to being inneficient, messes up with browser text
selection */} selection */}
{ElementOption({ type: "rectangle", children: "Rectangle" })} {ElementOption({ type: "rectangle", children: "Rectangle" })}
{ElementOption({ type: "ellipse", children: "Ellipse" })} {ElementOption({ type: "ellipse", children: "Ellipse" })}
{ElementOption({ type: "arrow", children: "Arrow" })} {ElementOption({ type: "arrow", children: "Arrow" })}
{ElementOption({ type: "text", children: "Text" })} {ElementOption({ type: "text", children: "Text" })}
{ElementOption({ type: "selection", children: "Selection" })} {ElementOption({ type: "selection", children: "Selection" })}
<canvas <canvas
id="canvas" id="canvas"
width={window.innerWidth} width={window.innerWidth}
height={window.innerHeight} height={window.innerHeight}
onClick={e => { onMouseDown={e => {
console.log("click"); const x = e.clientX - e.target.offsetLeft;
}} const y = e.clientY - e.target.offsetTop;
onMouseDown={e => { const element = newElement(this.state.elementType, x, y);
const x = e.clientX - e.target.offsetLeft;
const y = e.clientY - e.target.offsetTop;
const element = newElement(elementType, x, y);
if (elementType === "text") { if (this.state.elementType === "text") {
const text = prompt("What text do you want?"); const text = prompt("What text do you want?");
if (text === null) { if (text === null) {
return; 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); generateDraw(element);
elements.push(element); elements.push(element);
if (elementType === "text") { if (this.state.elementType === "text") {
setDraggingElement(null); this.setState({ draggingElement: null });
element.isSelected = true; element.isSelected = true;
} else { } else {
setDraggingElement(element); this.setState({ draggingElement: 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); 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;
if (elementType === "selection") { generateDraw(draggingElement);
setSelection(draggingElement);
} if (this.state.elementType === "selection") {
drawScene(); setSelection(draggingElement);
}} }
/> drawScene();
</div> };
);
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"); const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement); ReactDOM.render(<App />, rootElement);
const canvas = document.getElementById("canvas"); const canvas = document.getElementById("canvas");