Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix skipped map pan/zoom animation when switching location #2185

Merged
merged 2 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Line wrap the file at 100 chars. Th
- Add button to remove account and WireGuard key from history in the login screen.

### Fixed
- Fix missing map animation after selecting a new location in the desktop app.

#### Android
- Fix connect action button sometimes showing itself as "Cancel" instead of "Secure my connection"
for a few seconds.
Expand Down
41 changes: 30 additions & 11 deletions gui/src/renderer/components/SvgMap.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { geoMercator, GeoProjection } from 'd3-geo';
import rbush from 'rbush';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ComposableMap, Geographies, Geography, Marker, ZoomableGroup } from 'react-simple-maps';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { ComposableMap, Geographies, Geography, Marker } from 'react-simple-maps';

import geographyData from '../../../assets/geo/geometry.json';
import statesProvincesLinesData from '../../../assets/geo/states-provinces-lines.json';

import geometryTreeData from '../../../assets/geo/geometry.rbush.json';
import statesProvincesLinesTreeData from '../../../assets/geo/states-provinces-lines.rbush.json';
import { useScheduler } from '../../shared/scheduler';

interface IGeometryLeaf extends rbush.BBox {
id: string;
Expand Down Expand Up @@ -214,13 +213,6 @@ function SvgMap(props: IProps) {
);
const [visibleGeometry, visibleStatesProvincesLines] = useVisibleGeometry(viewportBboxes);

// react-simple-maps renders the map with zoom=1 the first render resulting in a transition from
// 1 to zoomLevel when it immediately renders a second time. This makes sure that transitions are
// disabled until after the second render.
const [enableTransition, setEnableTransition] = useState(false);
const enableTransitionScheduler = useScheduler();
useEffect(() => enableTransitionScheduler.schedule(() => setEnableTransition(true)), []);

const markerStyle = useMemo(
() => mergeRsmStyle({ default: { display: props.showMarker ? undefined : 'none' } }),
[props.showMarker],
Expand All @@ -242,7 +234,10 @@ function SvgMap(props: IProps) {
center={zoomCenter}
zoom={zoomLevel}
onTransitionEnd={removeOldViewportBboxes}
style={enableTransition ? zoomableGroupStyle : undefined}>
style={zoomableGroupStyle}
width={width}
height={height}
projection={projection}>
<Geographies geography={geographyData}>
{({ geographies }) => {
return visibleGeometry.map(({ id }) => (
Expand Down Expand Up @@ -274,3 +269,27 @@ function SvgMap(props: IProps) {
}

export default React.memo(SvgMap, sameProps);

// Workaround for issue where react-simple-maps does an animated zoom/pan when first loading the
// map. When this issue is resolved it can be removed:
// https://github.com/zcreativelabs/react-simple-maps/issues/228
interface IZoomableGroupProps extends React.SVGAttributes<SVGGElement> {
center: [number, number];
zoom: number;
width: number;
height: number;
projection: GeoProjection;
}

function ZoomableGroup(props: IZoomableGroupProps) {
const { height, width, center, zoom, projection, ...otherProps } = props;

const transform = useMemo(() => {
const [x, y] = projection(center) ?? [0, 0];
const translateX = width / 2 - x * zoom;
const translateY = height / 2 - y * zoom;
return `translate(${translateX} ${translateY}) scale(${zoom})`;
}, [projection, center, width, height, zoom]);

return <g transform={transform} {...otherProps} />;
}