From e22d51947ab75d909c14eab15d562ed4cdc3e7ec Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Sat, 25 Jan 2025 23:15:32 +0000 Subject: [PATCH] feat: center canvas on image open --- src/components/dialogs/OpenImage.tsx | 15 +++++++++++++-- src/context/Canvas.tsx | 27 ++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/components/dialogs/OpenImage.tsx b/src/components/dialogs/OpenImage.tsx index 1f73d6d..0eae14f 100644 --- a/src/components/dialogs/OpenImage.tsx +++ b/src/components/dialogs/OpenImage.tsx @@ -24,7 +24,7 @@ import VersionCombobox from "../VersionCombobox"; import { findBlockFromRgb } from "@/utils/findBlockFromRgb"; function OpenImage({ close }: DialogProps) { - const { version, setVersion, setBlocks } = useContext(CanvasContext); + const { version, setBlocks, setVersion, centerCanvas } = useContext(CanvasContext); const { setLoading } = useContext(LoadingContext); const { acceptedFiles, getRootProps, getInputProps } = useDropzone({ @@ -52,6 +52,8 @@ function OpenImage({ close }: DialogProps) { falling: false, }); + const [isFinished, setIsFinished] = useState(false); + useEffect(() => { if (acceptedFiles[0]) { const img = new Image(); @@ -96,6 +98,7 @@ function OpenImage({ close }: DialogProps) { const onSubmit = async () => { if (image) { + setIsFinished(false); setLoading(true); // Wait for loading indicator to appear await new Promise((resolve) => setTimeout(resolve, 100)); @@ -131,10 +134,18 @@ function OpenImage({ close }: DialogProps) { } setLoading(false); - close(); + setIsFinished(true); } }; + useEffect(() => { + if (!isFinished) return; + centerCanvas(); + close(); + + return () => setIsFinished(false); + }, [isFinished, centerCanvas, close]); + useEffect(() => { Object.keys(blockTypeCheckboxesChecked).forEach((property) => { const blocksWithProperty = Object.entries(blockData) diff --git a/src/context/Canvas.tsx b/src/context/Canvas.tsx index c9e112e..54af207 100644 --- a/src/context/Canvas.tsx +++ b/src/context/Canvas.tsx @@ -14,6 +14,7 @@ interface Context { setCoords: React.Dispatch>; setScale: React.Dispatch>; setVersion: React.Dispatch>; + centerCanvas: () => void; } interface Props { @@ -29,6 +30,7 @@ export const CanvasProvider = ({ children }: Props) => { const [scale, setScale] = useState(1); const [version, setVersion] = useState(1214); + // Get the farthest away blocks in each direction const canvasSize = useMemo(() => { let minX = Infinity, maxX = -Infinity; @@ -50,9 +52,32 @@ export const CanvasProvider = ({ children }: Props) => { }; }, [blocks]); + const centerCanvas = () => { + // Margin of 8 blocks on each side + const margin = 8 * 16; + + // Calculate the total width and height of the blocks, including margin + const blocksWidth = (canvasSize.maxX - canvasSize.minX) * 16 + margin * 2; + const blocksHeight = (canvasSize.maxY - canvasSize.minY) * 16 + margin * 2; + + // Calculate the scale to fit the blocks (with margin) within the stage + const scaleX = stageSize.width / blocksWidth; + const scaleY = stageSize.height / blocksHeight; + + // Use the smaller scale to fit the blocks inside the entire screen + const newScale = Math.min(scaleX, scaleY); + + // Calculate the coordinates to center the blocks in the middle of the stage + const newX = stageSize.width / 2 - ((blocksWidth - margin * 2) * newScale) / 2 - canvasSize.minX * 16 * newScale; + const newY = stageSize.height / 2 - ((blocksHeight - margin * 2) * newScale) / 2 - canvasSize.minY * 16 * newScale; + + setScale(newScale); + setCoords({ x: newX, y: newY }); + }; + return ( {children}