Implementing a SVG layout (aka whiteboard).

This commit is contained in:
Eric van der Vlist 2022-09-21 00:23:42 +02:00
parent 92700c3964
commit 7808b7db7e
4 changed files with 74 additions and 3 deletions

View File

@ -7,6 +7,7 @@ import Layer from '../slippy/layer';
import Slippy from '../slippy/slippy'; import Slippy from '../slippy/slippy';
import TiledMap from './tiled-map'; import TiledMap from './tiled-map';
import GetLocation from './get-location'; import GetLocation from './get-location';
import Whiteboard from './whiteboard';
const Map: react.FC<{}> = (props: {}) => { const Map: react.FC<{}> = (props: {}) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -28,6 +29,7 @@ const Map: react.FC<{}> = (props: {}) => {
<Fragment> <Fragment>
<Slippy> <Slippy>
<GetLocation /> <GetLocation />
<Whiteboard />
</Slippy> </Slippy>
<Layer> <Layer>
<TiledMap /> <TiledMap />

View File

@ -0,0 +1,32 @@
import React, { Fragment } from 'react';
import { useSelector } from 'react-redux';
import { MapState } from '../../store/map';
interface WhiteboardProps {
children?: any;
}
const Whiteboard: React.FC<WhiteboardProps> = (props: WhiteboardProps) => {
const whiteBoardState = useSelector(
(state: { map: MapState }) => state.map.whiteboard
);
return (
<svg width={window.innerWidth} height={window.innerHeight}>
<g
transform={`scale(${whiteBoardState.scale}) translate(${whiteBoardState.translation.x},${whiteBoardState.translation.y})`}
>
<circle
cx='0.7154276391666667'
cy='0.6137091783430285'
r={10 / whiteBoardState.scale}
fill='green'
vector-effect='non-scaling-size'
></circle>
{props.children}
</g>
</svg>
);
};
export default Whiteboard;

View File

@ -4,6 +4,7 @@ import { MapState } from '../../store/map';
import '../../theme/layer.css'; import '../../theme/layer.css';
const Layer: react.FC<{ const Layer: react.FC<{
children?: JSX.Element; children?: JSX.Element;
}> = (props: { children?: JSX.Element }) => { }> = (props: { children?: JSX.Element }) => {

View File

@ -17,9 +17,9 @@ export interface MapScope {
center: geoPoint; center: geoPoint;
zoom: number; zoom: number;
} }
const initialMapScope: MapScope = { var initialMapScope: MapScope = {
center: { lat: -37.8403508, lon: 77.5539501 }, center: { lat: -37.8403508, lon: 77.5539501 },
zoom: 13, zoom: 4,
}; };
// Derived properties // Derived properties
@ -37,11 +37,18 @@ export interface SlippyState {
translation: Point; translation: Point;
} }
// Properties needed to render the SVG whiteboard
export interface WhiteboardState {
scale: number;
translation: Point;
}
// Global state // Global state
export interface MapState { export interface MapState {
scope: MapScope; scope: MapScope;
tiles: TilesDescription; tiles: TilesDescription;
slippy: SlippyState; slippy: SlippyState;
whiteboard: WhiteboardState;
} }
/* /*
@ -87,6 +94,30 @@ export var initialMapState: MapState = {
}, },
zoom: 0, zoom: 0,
}, },
whiteboard: {
scale: 0,
translation: {
x: 0,
y: 0,
},
},
};
const evaluateWhiteboardViewBox = (
state: MapState,
visibleTileSize: number
) => {
// Update the whiteboard SVG viewBox
const scaleFactor = 2 ** state.scope.zoom;
state.whiteboard.scale = scaleFactor * tileSize;
state.whiteboard.translation.x =
(-state.tiles.first.x * visibleTileSize + state.slippy.translation.x) /
state.whiteboard.scale;
state.whiteboard.translation.y =
(-state.tiles.first.y * visibleTileSize + state.slippy.translation.y) /
state.whiteboard.scale;
}; };
export const evaluateStateFromScope = (state: MapState) => { export const evaluateStateFromScope = (state: MapState) => {
@ -122,6 +153,8 @@ export const evaluateStateFromScope = (state: MapState) => {
tilesCenterTargetLocation.x - tilesCenterActualLocation.x; tilesCenterTargetLocation.x - tilesCenterActualLocation.x;
state.slippy.translation.y = state.slippy.translation.y =
tilesCenterTargetLocation.y - tilesCenterActualLocation.y; tilesCenterTargetLocation.y - tilesCenterActualLocation.y;
evaluateWhiteboardViewBox(state, visibleTileSize);
}; };
evaluateStateFromScope(initialMapState); evaluateStateFromScope(initialMapState);
@ -146,7 +179,8 @@ export const reevaluateState = (state: MapState) => {
state.scope.zoom = state.tiles.zoom + Math.log2(state.slippy.scale); state.scope.zoom = state.tiles.zoom + Math.log2(state.slippy.scale);
// TODO: zoom level // Check if the state must be reevaluated
if ( if (
-state.slippy.translation.x < visibleTileSize || -state.slippy.translation.x < visibleTileSize ||
-state.slippy.translation.y < visibleTileSize || -state.slippy.translation.y < visibleTileSize ||
@ -158,6 +192,8 @@ export const reevaluateState = (state: MapState) => {
state.slippy.scale < Math.SQRT1_2 state.slippy.scale < Math.SQRT1_2
) { ) {
evaluateStateFromScope(state); evaluateStateFromScope(state);
} else {
evaluateWhiteboardViewBox(state, visibleTileSize);
} }
}; };