feat: zoom tool
This commit is contained in:
parent
ceb87c2088
commit
57064e71c7
3 changed files with 67 additions and 19 deletions
|
|
@ -37,6 +37,7 @@ function Canvas() {
|
||||||
|
|
||||||
const [blocks, setBlocks] = useState<Block[]>([]);
|
const [blocks, setBlocks] = useState<Block[]>([]);
|
||||||
|
|
||||||
|
const [holdingAlt, setHoldingAlt] = useState(false);
|
||||||
const [oldTool, setOldTool] = useState<Tool>("hand");
|
const [oldTool, setOldTool] = useState<Tool>("hand");
|
||||||
|
|
||||||
const updatedBlocks = useMemo(() => {
|
const updatedBlocks = useMemo(() => {
|
||||||
|
|
@ -64,6 +65,25 @@ function Canvas() {
|
||||||
};
|
};
|
||||||
}, [blocks]);
|
}, [blocks]);
|
||||||
|
|
||||||
|
const zoomToMousePosition = useCallback(
|
||||||
|
(newScale: number) => {
|
||||||
|
setCoords({
|
||||||
|
x: mousePosition.x - ((mousePosition.x - coords.x) / scale) * newScale,
|
||||||
|
y: mousePosition.y - ((mousePosition.y - coords.y) / scale) * newScale,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[coords, mousePosition, scale]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateCssCursor = useCallback(() => {
|
||||||
|
const cursorMapping: Partial<Record<Tool, string>> = {
|
||||||
|
hand: dragging ? "grabbing" : "grab",
|
||||||
|
zoom: holdingAlt ? "zoom-out" : "zoom-in",
|
||||||
|
};
|
||||||
|
|
||||||
|
setCssCursor(cursorMapping[tool] || "pointer");
|
||||||
|
}, [dragging, holdingAlt, tool, setCssCursor]);
|
||||||
|
|
||||||
const onToolUse = useCallback(() => {
|
const onToolUse = useCallback(() => {
|
||||||
switch (tool) {
|
switch (tool) {
|
||||||
case "pencil": {
|
case "pencil": {
|
||||||
|
|
@ -116,30 +136,34 @@ function Canvas() {
|
||||||
const onMouseDown = useCallback(() => {
|
const onMouseDown = useCallback(() => {
|
||||||
setDragging(true);
|
setDragging(true);
|
||||||
onToolUse();
|
onToolUse();
|
||||||
setCssCursor(tool === "hand" ? "grabbing" : "");
|
updateCssCursor();
|
||||||
}, [onToolUse, tool, setCssCursor]);
|
}, [onToolUse, updateCssCursor]);
|
||||||
|
|
||||||
const onMouseUp = () => {
|
const onMouseUp = useCallback(() => {
|
||||||
setDragging(false);
|
setDragging(false);
|
||||||
setCssCursor(tool === "hand" ? "grab" : "");
|
updateCssCursor();
|
||||||
};
|
}, [updateCssCursor]);
|
||||||
|
|
||||||
const onWheel = useCallback(
|
const onWheel = useCallback(
|
||||||
(e: React.WheelEvent) => {
|
(e: React.WheelEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const scaleChange = e.deltaY > 0 ? -0.1 : 0.1;
|
const scaleChange = e.deltaY > 0 ? -0.1 : 0.1;
|
||||||
const newScale = Math.min(Math.max(scale + scaleChange * scale, 0.1), 32);
|
const newScale = Math.min(Math.max(scale + scaleChange * scale, 0.1), 32);
|
||||||
|
|
||||||
setScale(newScale);
|
setScale(newScale);
|
||||||
setCoords({
|
zoomToMousePosition(newScale);
|
||||||
x: mousePosition.x - ((mousePosition.x - coords.x) / scale) * newScale,
|
|
||||||
y: mousePosition.y - ((mousePosition.y - coords.y) / scale) * newScale,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[scale, coords, mousePosition]
|
[scale, zoomToMousePosition]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onClick = useCallback(() => {
|
||||||
|
if (tool === "zoom") {
|
||||||
|
const scaleChange = holdingAlt ? -0.1 : 0.1;
|
||||||
|
const newScale = Math.min(Math.max(scale + scaleChange * scale, 0.1), 32);
|
||||||
|
setScale(newScale);
|
||||||
|
zoomToMousePosition(newScale);
|
||||||
|
}
|
||||||
|
}, [tool, holdingAlt, scale, zoomToMousePosition]);
|
||||||
|
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case " ": // Space
|
case " ": // Space
|
||||||
|
|
@ -157,15 +181,27 @@ function Canvas() {
|
||||||
case "3":
|
case "3":
|
||||||
setTool("eraser");
|
setTool("eraser");
|
||||||
break;
|
break;
|
||||||
|
case "4":
|
||||||
|
setTool("zoom");
|
||||||
|
break;
|
||||||
|
case "Alt":
|
||||||
|
setHoldingAlt(true);
|
||||||
|
setCssCursor("zoom-out");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKeyUp = (e: KeyboardEvent) => {
|
const onKeyUp = (e: KeyboardEvent) => {
|
||||||
if (e.key == " ") {
|
switch (e.key) {
|
||||||
// Space
|
case " ": // Space
|
||||||
setDragging(false);
|
setDragging(false);
|
||||||
setCssCursor("grab");
|
setCssCursor("grab");
|
||||||
setTool(oldTool);
|
setTool(oldTool);
|
||||||
|
break;
|
||||||
|
case "Alt":
|
||||||
|
setHoldingAlt(false);
|
||||||
|
setCssCursor("zoom-in");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -203,6 +239,7 @@ function Canvas() {
|
||||||
onMouseDown={onMouseDown}
|
onMouseDown={onMouseDown}
|
||||||
onMouseUp={onMouseUp}
|
onMouseUp={onMouseUp}
|
||||||
onWheel={onWheel}
|
onWheel={onWheel}
|
||||||
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<Container x={coords.x} y={coords.y} scale={scale}>
|
<Container x={coords.x} y={coords.y} scale={scale}>
|
||||||
<Blocks blocks={blocks} setBlocks={setBlocks} textures={textures} image={image} imageDimensions={imageDimensions} />
|
<Blocks blocks={blocks} setBlocks={setBlocks} textures={textures} image={image} imageDimensions={imageDimensions} />
|
||||||
|
|
@ -19,7 +19,18 @@ export const ToolProvider = ({ children }: Props) => {
|
||||||
const [cssCursor, setCssCursor] = useState("pointer");
|
const [cssCursor, setCssCursor] = useState("pointer");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCssCursor(tool === "hand" ? "grab" : "pointer");
|
switch (tool) {
|
||||||
|
case "hand":
|
||||||
|
setCssCursor("grab");
|
||||||
|
break;
|
||||||
|
case "zoom":
|
||||||
|
setCssCursor("zoom-in");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
setCssCursor("pointer");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}, [tool]);
|
}, [tool]);
|
||||||
|
|
||||||
return <ToolContext.Provider value={{ tool, selectedBlock, cssCursor, setTool, setSelectedBlock, setCssCursor }}>{children}</ToolContext.Provider>;
|
return <ToolContext.Provider value={{ tool, selectedBlock, cssCursor, setTool, setSelectedBlock, setCssCursor }}>{children}</ToolContext.Provider>;
|
||||||
|
|
|
||||||
2
src/types.d.ts
vendored
2
src/types.d.ts
vendored
|
|
@ -21,7 +21,7 @@ interface Block extends Position {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tool = "hand" | "pencil" | "eraser";
|
type Tool = "hand" | "pencil" | "eraser" | "zoom";
|
||||||
|
|
||||||
interface Settings {
|
interface Settings {
|
||||||
grid: boolean;
|
grid: boolean;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue