Mouse move tracked outside window!
This commit is contained in:
parent
9aaaa24426
commit
457800caa3
279
src/index.js
279
src/index.js
@ -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");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user