diff --git a/src/redux-state/slices/island.ts b/src/redux-state/slices/island.ts index 76a4f2b28..38b4414c9 100644 --- a/src/redux-state/slices/island.ts +++ b/src/redux-state/slices/island.ts @@ -43,7 +43,7 @@ export type IslandState = { const initialState: IslandState = { mode: "default", - overlay: "none", + overlay: "dark", realms: REALMS, stakingRealmId: null, displayedRealmId: null, diff --git a/src/shared/utils/island.ts b/src/shared/utils/island.ts index 71ac74f6f..16ccdf921 100644 --- a/src/shared/utils/island.ts +++ b/src/shared/utils/island.ts @@ -1,4 +1,6 @@ import assert from "assert" +import { Stage } from "konva/lib/Stage" +import { ISLAND_BOX } from "shared/constants" type Dimensions = { width: number @@ -14,6 +16,11 @@ type RealmRenderData = { paths: { data: string }[] } +type Coordinates = { + x: number + y: number +} + export function limitToBounds(val: number, min: number, max: number) { if (val < min) return min if (val > max) return max @@ -110,3 +117,36 @@ export function createBackgroundMask( ctx.drawImage(bgImage, 0, 0) return canvas } + +export function calculateNewIslandScale( + newValue: number, + minScale: number +): number { + const newScale = limitToBounds(newValue, minScale, Math.max(0.45, minScale)) + return newScale +} + +export function calculateIslandPosition( + stage: Stage, + newScale: number, + targetX: number, + targetY: number +): Coordinates { + const maxX = ISLAND_BOX.width - stage.width() / newScale + const maxY = ISLAND_BOX.height - stage.height() / newScale + + // Force bounds while zooming in/out + const newX = limitToBounds(targetX, -maxX * newScale, 0) + const newY = limitToBounds(targetY, -maxY * newScale, 0) + + return { x: newX, y: newY } +} + +export function getCurrentCanvasPosition( + positionX: number, + positionY: number, + zoom: number +): Coordinates { + const canvasPosition = { x: positionX / zoom, y: positionY / zoom } + return canvasPosition +} diff --git a/src/ui/Controls/IslandControl.tsx b/src/ui/Controls/IslandControl.tsx index 36c1dd195..81b393fb5 100644 --- a/src/ui/Controls/IslandControl.tsx +++ b/src/ui/Controls/IslandControl.tsx @@ -1,6 +1,14 @@ -import React, { useState } from "react" +import React from "react" +import { + selectIslandOverlay, + setIslandOverlay, + useDappDispatch, + useDappSelector, +} from "redux-state" + +function IslandControlIcon() { + const isOverlay = useDappSelector(selectIslandOverlay) -function IslandControlIcon({ isOverlay }: { isOverlay: boolean }) { return ( - + - + - + @@ -49,16 +72,19 @@ function IslandControlIcon({ isOverlay }: { isOverlay: boolean }) { } export default function IslandControl() { - const [isOverlay, setIsOverlay] = useState(false) + const isOverlay = useDappSelector(selectIslandOverlay) + const dispatch = useDappDispatch() return ( <>
diff --git a/src/ui/Controls/ZoomControls.tsx b/src/ui/Controls/ZoomControls.tsx index 93ca2a3f9..7c794e5e3 100644 --- a/src/ui/Controls/ZoomControls.tsx +++ b/src/ui/Controls/ZoomControls.tsx @@ -1,14 +1,64 @@ import React from "react" import zoomIn from "shared/assets/icons/m/zoom-in.svg" import zoomOut from "shared/assets/icons/m/zoom-out.svg" +import { Stage } from "konva/lib/Stage" + +import { useDappDispatch, setIslandZoomLevel } from "redux-state" +import { + calculateIslandPosition, + calculateNewIslandScale, + getCurrentCanvasPosition, +} from "shared/utils" import ZoomControl from "./ZoomControl" -export default function ZoomControls() { +type ZoomControlsProps = { + stage: Stage + minScale: number +} + +export default function ZoomControls({ stage, minScale }: ZoomControlsProps) { + const dispatch = useDappDispatch() + + const zoomHandler = (increase: boolean) => { + const zoom = stage.scaleX() + const scaleBy = 1.05 + + const center = { + x: stage.width() / 2, + y: stage.height() / 2, + } + + // Get current center related to screen + const canvasPosition = getCurrentCanvasPosition( + center.x - stage.x(), + center.y - stage.y(), + zoom + ) + + const newScale = calculateNewIslandScale( + increase ? zoom * scaleBy : zoom / scaleBy, + minScale + ) + + const newPosition = { + x: center.x - canvasPosition.x * newScale, + y: center.y - canvasPosition.y * newScale, + } + + // Force bounds while zooming in/out + stage.absolutePosition( + calculateIslandPosition(stage, newScale, newPosition.x, newPosition.y) + ) + + // Update the stage scale + dispatch(setIslandZoomLevel(newScale)) + } + return ( <>
- - + zoomHandler(true)} /> + zoomHandler(false)} />