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 [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
|
||||
setDragging(false);
|
||||
setCssCursor("grab");
|
||||
setTool(oldTool);
|
||||
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} />
|
||||
|
|
@ -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
2
src/types.d.ts
vendored
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue