86 lines
2.1 KiB
TypeScript
86 lines
2.1 KiB
TypeScript
import { useAtom } from 'jotai';
|
|
import react from 'react';
|
|
import TileSet from './TileSet';
|
|
import { Point, TileKeyObject } from './types';
|
|
import { coordinateSystemAtom } from './Map';
|
|
|
|
export interface TiledLayerProperties {
|
|
/**
|
|
* A key identifying the top left tile
|
|
*/
|
|
keyObject: TileKeyObject;
|
|
/**
|
|
* The translation to apply.
|
|
*/
|
|
shift: Point;
|
|
/**
|
|
* The zoom to apply. If equal to 256 (the tile size), the layer is considered active and should add tiles which are in its viewport
|
|
*/
|
|
zoom: number;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param props
|
|
* @returns A layer of tiles.
|
|
* This component wraps a `<TileSet>` in an SVG `<g>` element taking care of the scale and translation.
|
|
*
|
|
*/
|
|
export const TiledLayer: react.FC<TiledLayerProperties> = (
|
|
props: TiledLayerProperties
|
|
) => {
|
|
const [coordinateSystem] = useAtom(coordinateSystemAtom);
|
|
const viewPort =
|
|
props.zoom === 256
|
|
? {
|
|
topLeft: {
|
|
x:
|
|
props.keyObject.x +
|
|
Math.floor(
|
|
-coordinateSystem.shift.x / coordinateSystem.zoom / 256
|
|
),
|
|
y:
|
|
props.keyObject.y +
|
|
Math.floor(
|
|
-coordinateSystem.shift.y / coordinateSystem.zoom / 256
|
|
),
|
|
},
|
|
bottomRight: {
|
|
x:
|
|
props.keyObject.x +
|
|
Math.ceil(
|
|
(-coordinateSystem.shift.x + window.innerWidth) /
|
|
coordinateSystem.zoom /
|
|
256
|
|
) -
|
|
1,
|
|
y:
|
|
props.keyObject.y +
|
|
Math.ceil(
|
|
(-coordinateSystem.shift.y + window.innerHeight) /
|
|
coordinateSystem.zoom /
|
|
256
|
|
) -
|
|
1,
|
|
},
|
|
}
|
|
: undefined;
|
|
return (
|
|
<g
|
|
transform={`scale(${props.zoom}) translate(${props.shift.x}, ${props.shift.y})`}
|
|
>
|
|
<TileSet
|
|
keyObject={{
|
|
provider: props.keyObject.provider,
|
|
zoomLevel: props.keyObject.zoomLevel,
|
|
x: 0,
|
|
y: 0,
|
|
}}
|
|
viewPort={viewPort}
|
|
/>
|
|
</g>
|
|
);
|
|
};
|
|
|
|
export default TiledLayer;
|