Using maps instead of objects to store tiles and removing the reinititialization when TiledLayer properties are updated.

This commit is contained in:
Eric van der Vlist 2022-10-19 14:53:22 +02:00
parent b5dd10e920
commit 4238ce0938
3 changed files with 165 additions and 10 deletions

View File

@ -0,0 +1,138 @@
import { renderHook, act } from '@testing-library/react';
import { useAtom } from 'jotai';
import { coordinateSystemAtom, relativeCoordinateSystemAtom } from './Map';
describe('The Map component coordinate system', () => {
test('is initialized', () => {
const { result } = renderHook(() => useAtom(coordinateSystemAtom));
expect(result.current[0]).toMatchInlineSnapshot(`
Object {
"shift": Object {
"x": 0,
"y": 0,
},
"zoom": 1,
}
`);
});
test('reacts to a translation', () => {
const { result } = renderHook(() => [
useAtom(coordinateSystemAtom),
useAtom(relativeCoordinateSystemAtom),
]);
act(() => {
result.current[0][1]({
shift: {
x: 0,
y: 0,
},
zoom: 1,
} as any);
result.current[1][1]({
deltaShift: { x: 5, y: 1 },
zoomCenter: null,
deltaZoom: null,
} as any);
});
expect(result.current[0][0]).toMatchInlineSnapshot(`
Object {
"shift": Object {
"x": 5,
"y": 1,
},
"zoom": 1,
}
`);
});
test('reacts to a zoom on its origin', () => {
const { result } = renderHook(() => [
useAtom(coordinateSystemAtom),
useAtom(relativeCoordinateSystemAtom),
]);
act(() => {
result.current[0][1]({
shift: {
x: 0,
y: 0,
},
zoom: 1,
} as any);
result.current[1][1]({
deltaShift: null,
zoomCenter: null,
deltaZoom: 5,
} as any);
});
expect(result.current[0][0]).toMatchInlineSnapshot(`
Object {
"shift": Object {
"x": 0,
"y": 0,
},
"zoom": 5,
}
`);
});
test('reacts to a zoom around another point', () => {
const { result } = renderHook(() => [
useAtom(coordinateSystemAtom),
useAtom(relativeCoordinateSystemAtom),
]);
act(() => {
result.current[0][1]({
shift: {
x: 0,
y: 0,
},
zoom: 1,
} as any);
result.current[1][1]({
deltaShift: null,
zoomCenter: { x: 2, y: 2 },
deltaZoom: 5,
} as any);
});
expect(result.current[0][0]).toMatchInlineSnapshot(`
Object {
"shift": Object {
"x": -8,
"y": -8,
},
"zoom": 5,
}
`);
});
test('reacts to a zoom (around another point) and a translation', () => {
const { result } = renderHook(() => [
useAtom(coordinateSystemAtom),
useAtom(relativeCoordinateSystemAtom),
]);
act(() => {
result.current[0][1]({
shift: {
x: 0,
y: 0,
},
zoom: 1,
} as any);
result.current[1][1]({
deltaShift: { x: 1, y: 3 },
zoomCenter: { x: 2, y: 2 },
deltaZoom: 5,
} as any);
});
expect(result.current[0][0]).toMatchInlineSnapshot(`
Object {
"shift": Object {
"x": -7,
"y": -5,
},
"zoom": 5,
}
`);
});
});

View File

@ -120,7 +120,7 @@ describe('The TiledLayer component ', () => {
</body> </body>
`); `);
}); });
test('is reinitialized if its key isObject updated', () => { test('is not reinitialized if its key isObject updated', () => {
const { baseElement, rerender } = render( const { baseElement, rerender } = render(
<svg> <svg>
<TiledLayer <TiledLayer
@ -144,6 +144,27 @@ describe('The TiledLayer component ', () => {
<body> <body>
<div> <div>
<svg> <svg>
<g
transform="translate(1, 2)"
>
<text>
{"provider":"osm","zoomLevel":10,"x":6,"y":10}
</text>
</g>
<g
transform="translate(2, 2)"
>
<text>
{"provider":"osm","zoomLevel":10,"x":7,"y":10}
</text>
</g>
<g
transform="translate(3, 2)"
>
<text>
{"provider":"osm","zoomLevel":10,"x":8,"y":10}
</text>
</g>
<g <g
transform="translate(5, 0)" transform="translate(5, 0)"
> >

View File

@ -30,12 +30,7 @@ export interface TiledLayerProperties {
export const TiledLayer: react.FC<TiledLayerProperties> = memo( export const TiledLayer: react.FC<TiledLayerProperties> = memo(
(props: TiledLayerProperties) => { (props: TiledLayerProperties) => {
console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`); console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);
const tiles = useRef<any>({}); const tiles = useRef<any>(new Map());
const previousKeyObject = useRef<TileKeyObject>(props.keyObject);
if (!isEqual(props.keyObject, previousKeyObject.current)) {
previousKeyObject.current = props.keyObject;
tiles.current = {};
}
range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map( range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map(
(row) => { (row) => {
@ -48,8 +43,9 @@ export const TiledLayer: react.FC<TiledLayerProperties> = memo(
y: props.keyObject.y + row, y: props.keyObject.y + row,
}; };
const key = tileUri(keyObject); const key = tileUri(keyObject);
if (!Object.hasOwn(tiles.current, key)) { if (!tiles.current.has(key)) {
tiles.current[key] = ( tiles.current.set(
key,
<g key={key} transform={`translate(${col}, ${row})`}> <g key={key} transform={`translate(${col}, ${row})`}>
{props.tileFactory(keyObject)} {props.tileFactory(keyObject)}
</g> </g>
@ -60,7 +56,7 @@ export const TiledLayer: react.FC<TiledLayerProperties> = memo(
} }
); );
return <>{Object.values(tiles.current)}</>; return <>{Array.from(tiles.current.values())}</>;
}, },
isEqual isEqual
); );