drawing lines (shift to draw multiple)
This commit is contained in:
parent
4b269e5757
commit
7fe8047f23
20
package-lock.json
generated
20
package-lock.json
generated
@ -10,10 +10,12 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"css-loader": "^6.7.3",
|
||||
@ -212,6 +214,12 @@
|
||||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.202",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
|
||||
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
|
||||
@ -2331,8 +2339,7 @@
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
@ -4522,6 +4529,12 @@
|
||||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.202",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
|
||||
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
|
||||
@ -6150,8 +6163,7 @@
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
|
@ -10,6 +10,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"css-loader": "^6.7.3",
|
||||
@ -28,6 +29,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { useWindowSize } from '@uidotdev/usehooks';
|
||||
import { FC, useMemo, useState } from 'react';
|
||||
import { useMouse, useWindowSize } from '@uidotdev/usehooks';
|
||||
import { FC, MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import './grid.scss';
|
||||
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
@ -15,6 +17,18 @@ interface Range {
|
||||
y1: number;
|
||||
}
|
||||
|
||||
function pointsToRange(p0: Point, p1: Point): Range {
|
||||
return { x0: p0.x, y0: p0.y, x1: p1.x, y1: p1.y };
|
||||
}
|
||||
|
||||
function snapToGrid(p: Point, gap: number): Point {
|
||||
// snap a point to a grid
|
||||
return {
|
||||
x: gap * Math.round(p.x / gap),
|
||||
y: gap * Math.round(p.y / gap),
|
||||
};
|
||||
}
|
||||
|
||||
const ORIGIN: Point = { x: 0, y: 0 };
|
||||
|
||||
function useWindowSizeCustom() {
|
||||
@ -40,73 +54,130 @@ function useRange(offset: Point): Range {
|
||||
return range;
|
||||
}
|
||||
|
||||
function useMouseInRange(range: Range): Point {
|
||||
const [mouse, _] = useMouse();
|
||||
return { x: mouse.x + range.x0, y: mouse.y + range.y0 };
|
||||
}
|
||||
|
||||
const Grid: FC = () => {
|
||||
const [offset, setOffset] = useState<Point>(ORIGIN);
|
||||
const range = useRange(offset);
|
||||
const mousePoint = useMouseInRange(range);
|
||||
|
||||
const gap = 100;
|
||||
|
||||
const nodes = useMemo(() => {
|
||||
const nodes = [];
|
||||
for (let x = range.x0; x <= range.x1; x += gap) {
|
||||
for (let y = range.y0; y <= range.y1; y += gap) {
|
||||
nodes.push({ x, y });
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}, [range, gap]);
|
||||
const GAP = 20;
|
||||
|
||||
const gridLines = useMemo(() => {
|
||||
const lines = [];
|
||||
for (let x = range.x0; x <= range.x1; x += gap) {
|
||||
for (let x = range.x0; x <= range.x1; x += GAP) {
|
||||
lines.push({ ...range, x0: x, x1: x });
|
||||
}
|
||||
for (let y = range.y0; y <= range.y1; y += gap) {
|
||||
for (let y = range.y0; y <= range.y1; y += GAP) {
|
||||
lines.push({ ...range, y0: y, y1: y });
|
||||
}
|
||||
|
||||
return lines;
|
||||
}, [range, gap]);
|
||||
}, [range, GAP]);
|
||||
|
||||
const nodeElements = useMemo(() => {
|
||||
return nodes.map((point) => (
|
||||
<circle cx={point.x} cy={point.y} r="5" fill="#123456" />
|
||||
));
|
||||
}, [nodes]);
|
||||
const [userLines, setUserLines] = useState<Range[]>([]);
|
||||
const [userStartPoint, setUserStartPoint] = useState<Point | null>(null);
|
||||
|
||||
const gridLineElements = useMemo(() => {
|
||||
return gridLines.map((line) => (
|
||||
return gridLines.map((line, idx) => (
|
||||
<line
|
||||
key={idx}
|
||||
className="grid-line"
|
||||
x1={line.x0}
|
||||
y1={line.y0}
|
||||
x2={line.x1}
|
||||
y2={line.y1}
|
||||
stroke="#123456"
|
||||
stroke-width="1"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
));
|
||||
}, [gridLines]);
|
||||
|
||||
const mid = useMemo(() => {
|
||||
return {
|
||||
x: (range.x1 - range.x0) / 2,
|
||||
y: (range.y1 - range.y0) / 2,
|
||||
};
|
||||
}, [range]);
|
||||
const userLineIndicatorElement = useMemo(() => {
|
||||
if (lodash.isNull(userStartPoint)) return null;
|
||||
const point = snapToGrid(mousePoint, GAP);
|
||||
return (
|
||||
<line
|
||||
x1={userStartPoint.x}
|
||||
y1={userStartPoint.y}
|
||||
x2={point.x}
|
||||
y2={point.y}
|
||||
stroke="#ff0000"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
);
|
||||
}, [userStartPoint, mousePoint]);
|
||||
const userPointIndicatorElement = useMemo(() => {
|
||||
const point = snapToGrid(mousePoint, GAP);
|
||||
return (
|
||||
<circle
|
||||
className="node"
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r="3"
|
||||
fill="#ff0000"
|
||||
/>
|
||||
);
|
||||
}, [userStartPoint, mousePoint]);
|
||||
|
||||
const userLineElements = useMemo(() => {
|
||||
return userLines.map((line, idx) => (
|
||||
<line
|
||||
key={idx}
|
||||
className="grid-line"
|
||||
x1={line.x0}
|
||||
y1={line.y0}
|
||||
x2={line.x1}
|
||||
y2={line.y1}
|
||||
stroke="#ff0000"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
));
|
||||
}, [userLines]);
|
||||
|
||||
const onGridClick = useCallback(
|
||||
(event: MouseEvent<SVGSVGElement>) => {
|
||||
console.log('grid click', event);
|
||||
const pointRaw = { x: event.clientX, y: event.clientY };
|
||||
const point = snapToGrid(pointRaw, GAP);
|
||||
|
||||
if (lodash.isEqual(userStartPoint, point)) return;
|
||||
if (lodash.isNull(userStartPoint)) {
|
||||
setUserStartPoint(point);
|
||||
return;
|
||||
}
|
||||
|
||||
const line = pointsToRange(userStartPoint, point);
|
||||
setUserLines([...userLines, line]);
|
||||
|
||||
if (event.shiftKey == false) {
|
||||
setUserStartPoint(null);
|
||||
} else {
|
||||
setUserStartPoint(point);
|
||||
}
|
||||
},
|
||||
[userStartPoint, setUserStartPoint, setUserLines, GAP]
|
||||
);
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="grid"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox={`${range.x0} ${range.y0} ${range.x1} ${range.y1}`}
|
||||
onClick={onGridClick}
|
||||
>
|
||||
{nodeElements}
|
||||
{gridLineElements}
|
||||
{userLineElements}
|
||||
{userLineIndicatorElement}
|
||||
{userPointIndicatorElement}
|
||||
<text
|
||||
x={range.x0 + 20}
|
||||
y={range.y0 + 60} // of baseline
|
||||
fill="#ffffff"
|
||||
font-size="40px"
|
||||
fontSize="40px"
|
||||
>
|
||||
GRID
|
||||
</text>
|
||||
|
Loading…
Reference in New Issue
Block a user