feat: zoom tool

This commit is contained in:
trafficlunar 2024-12-15 13:46:28 +00:00
parent ceb87c2088
commit 57064e71c7
3 changed files with 67 additions and 19 deletions

View file

@ -37,6 +37,7 @@ function Canvas() {
const [blocks, setBlocks] = useState<Block[]>([]);
const [holdingAlt, setHoldingAlt] = useState(false);
const [oldTool, setOldTool] = useState<Tool>("hand");
const updatedBlocks = useMemo(() => {
@ -64,6 +65,25 @@ function Canvas() {
};
}, [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(() => {
switch (tool) {
case "pencil": {
@ -116,30 +136,34 @@ function Canvas() {
const onMouseDown = useCallback(() => {
setDragging(true);
onToolUse();
setCssCursor(tool === "hand" ? "grabbing" : "");
}, [onToolUse, tool, setCssCursor]);
updateCssCursor();
}, [onToolUse, updateCssCursor]);
const onMouseUp = () => {
const onMouseUp = useCallback(() => {
setDragging(false);
setCssCursor(tool === "hand" ? "grab" : "");
};
updateCssCursor();
}, [updateCssCursor]);
const onWheel = useCallback(
(e: React.WheelEvent) => {
e.preventDefault();
const scaleChange = e.deltaY > 0 ? -0.1 : 0.1;
const newScale = Math.min(Math.max(scale + scaleChange * scale, 0.1), 32);
setScale(newScale);
setCoords({
x: mousePosition.x - ((mousePosition.x - coords.x) / scale) * newScale,
y: mousePosition.y - ((mousePosition.y - coords.y) / scale) * newScale,
});
zoomToMousePosition(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) => {
switch (e.key) {
case " ": // Space
@ -157,15 +181,27 @@ function Canvas() {
case "3":
setTool("eraser");
break;
case "4":
setTool("zoom");
break;
case "Alt":
setHoldingAlt(true);
setCssCursor("zoom-out");
break;
}
};
const onKeyUp = (e: KeyboardEvent) => {
if (e.key == " ") {
// Space
switch (e.key) {
case " ": // Space
setDragging(false);
setCssCursor("grab");
setTool(oldTool);
break;
case "Alt":
setHoldingAlt(false);
setCssCursor("zoom-in");
break;
}
};
@ -203,6 +239,7 @@ function Canvas() {
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onWheel={onWheel}
onClick={onClick}
>
<Container x={coords.x} y={coords.y} scale={scale}>
<Blocks blocks={blocks} setBlocks={setBlocks} textures={textures} image={image} imageDimensions={imageDimensions} />

View file

@ -19,7 +19,18 @@ export const ToolProvider = ({ children }: Props) => {
const [cssCursor, setCssCursor] = useState("pointer");
useEffect(() => {
setCssCursor(tool === "hand" ? "grab" : "pointer");
switch (tool) {
case "hand":
setCssCursor("grab");
break;
case "zoom":
setCssCursor("zoom-in");
break;
default:
setCssCursor("pointer");
break;
}
}, [tool]);
return <ToolContext.Provider value={{ tool, selectedBlock, cssCursor, setTool, setSelectedBlock, setCssCursor }}>{children}</ToolContext.Provider>;

2
src/types.d.ts vendored
View file

@ -21,7 +21,7 @@ interface Block extends Position {
name: string;
}
type Tool = "hand" | "pencil" | "eraser";
type Tool = "hand" | "pencil" | "eraser" | "zoom";
interface Settings {
grid: boolean;