Attempt to add map bounds

- Basic animation (standard ball bounce) was placed on the parent of the map
- A state MapBoundReached tracks which bound (N, S, W or E) was reached
- This state variable was then used to conditionally apply the CSS animation

- How could we detect the end of the animation to remove it from the DOM after the animation played once?
- try and add an eventListener on the parent of the map using ref and useEffect 
- try and use the onAnimationEnd prop
the territory shortcuts seem to trigger this animationEnd event 
- try and differentiate between territory shortcut animation and map bound animation using
use isPanning 
- use isDragging 

- Now that the animation end event is being detected properly and its not interfering with the territory shortcuts, moving back to
- Getting the bounds to work for all directions (N, S, W, E)
- clunky feel and can’t get it to trigger accurately
- Making the animation feel real (right not it’s just a random bounce)

- Decided to attempt to use the transition built-into the ReactMapGL API, this was also not leading to reliable results.
This commit is contained in:
Vim USDS 2022-09-19 18:27:36 -07:00
parent f5f4330919
commit 38ab640f4e
4 changed files with 168 additions and 10 deletions

View file

@ -1,6 +1,16 @@
@use '../styles/design-system.scss' as *;
@import "./utils.scss";
.scrollDownBounce {
animation: bounce2 2s ease 1;
clip-path: inset(30px 0 0 0);
@keyframes bounce2 {
0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
40% {transform: translateY(-30px);}
60% {transform: translateY(-15px);}
}
}
.j40Map {
// width < 1024

View file

@ -1,5 +1,7 @@
declare namespace J40MapModuleScssNamespace {
export interface IJ40MapModuleScss {
scrollDownBounce: string;
// main J40 map style
j40Map: string;

View file

@ -12,7 +12,8 @@ import ReactMapGL, {
Popup,
FlyToInterpolator,
FullscreenControl,
MapRef} from 'react-map-gl';
MapRef,
LinearInterpolator} from 'react-map-gl';
import {useIntl} from 'gatsby-plugin-intl';
import bbox from '@turf/bbox';
import * as d3 from 'd3-ease';
@ -58,6 +59,8 @@ export interface IDetailViewInterface {
properties: constants.J40Properties,
};
type MapBoundaryReached = 'top' | 'right' | 'bottom' | 'left' | '';
const J40Map = ({location}: IJ40Interface) => {
/**
* Initializes the zoom, and the map's center point (lat, lng) via the URL hash #{z}/{lat}/{long}
@ -81,6 +84,10 @@ const J40Map = ({location}: IJ40Interface) => {
zoom: zoom && parseFloat(zoom) ? parseFloat(zoom) : constants.GLOBAL_MIN_ZOOM,
});
/**
* Map selection state variables:
*/
const [selectedFeature, setSelectedFeature] = useState<MapboxGeoJSONFeature>();
const [detailViewData, setDetailViewData] = useState<IDetailViewInterface>();
const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false);
@ -88,6 +95,28 @@ const J40Map = ({location}: IJ40Interface) => {
const [isMobileMapState, setIsMobileMapState] = useState<boolean>(false);
const [censusSelected, setCensusSelected] = useState<boolean>(true);
/**
* Map Boundary reached state variables:
*/
// const mapContainerRef = useRef(null);
// const [mapBoundaryReached, setMapBoundaryReached] = useState<MapBoundaryReached>('');
const [isMapBeingDragged, setIsMapBeingDragged] = useState<boolean>(false);
// useEffect(() => {
// const onAnimationEnd = () => {
// setMapBoundaryReached('');
// };
// const mapContainer = mapContainerRef.current;
// mapContainer.addEventListener('animationend', onAnimationEnd);
// return () => {
// mapContainer.removeEventListener('animationend', onAnimationEnd);
// };
// }, []);
// In order to detect that the layer has been toggled (between census and tribal),
// this state variable will hold that information
const [layerToggled, setLayerToggled] = useState<boolean>(false);
@ -324,11 +353,67 @@ const J40Map = ({location}: IJ40Interface) => {
setGeolocationInProgress(true);
};
/**
* This function will set the map boundaries during the dragging of the map. If a user attempts to
* drag the map past the boundaries, an animation will play to mimic the end of the end has been
* reached.
*
* @param {ViewportProps} viewState
* @return {} setViewport
*/
const onViewportChange = (viewState:ViewportProps) => {
// console.log('🚀 ~ file: J40Map.tsx ~ line 355 ~ onViewportChange ~ viewState', viewState);
let mapBoundaryReached:MapBoundaryReached = '';
if (isMapBeingDragged) {
if (
viewState.longitude &&
viewState.longitude > constants.MAP_BOUND_MIN_LONGITUDE &&
viewState.longitude < 180 ) {
viewState.longitude = constants.MAP_BOUND_MIN_LONGITUDE;
mapBoundaryReached= 'left';
} else if ( viewState.longitude && viewState.longitude > constants.MAP_BOUND_MAX_LONGITUDE ) {
viewState.longitude = constants.MAP_BOUND_MAX_LONGITUDE;
mapBoundaryReached= 'right';
}
if ( viewState.latitude && viewState.latitude < constants.MAP_BOUND_MIN_LATITUDE ) {
mapBoundaryReached= 'bottom';
viewState.latitude = constants.MAP_BOUND_MIN_LATITUDE;
}
if ( viewState.latitude && viewState.latitude > constants.MAP_BOUND_MAX_LATITUDE ) {
viewState.latitude = constants.MAP_BOUND_MAX_LATITUDE;
mapBoundaryReached= 'bottom';
}
}
return mapBoundaryReached ? setViewport({
...viewState,
transitionDuration: 1000,
transitionInterpolator: new LinearInterpolator(),
transitionEasing: d3.easeBounce,
}) : setViewport(viewState);
};
// Set mapbox base layer:
const mapBoxBaseLayer = 'tl' in flags ? `mapbox://styles/justice40/cl2qimpi2000014qeb1egpox8` : `mapbox://styles/justice40/cl5mp95tu000k14lpl21spgny`;
return (
<>
<Grid desktop={{col: 9}} className={styles.j40Map}>
<Grid
desktop={{col: 9}}
// className={mapBoundaryReached === 'bottom' ?
// `${styles.j40Map} ${styles.scrollDownBounce}` :
// styles.j40Map}
className={styles.j40Map}
// onAnimationEnd={(e) => {
// // Todo: Document how both animation types will trigger this callback
// // console.log('🚀 ~ file: J40Map.tsx ~ line 394 ~ J40Map ~ e', e);
// console.log('animation ended');
// setMapBoundaryReached('');
// }}
>
{/**
* Note:
* The MapSearch component is no longer used in this location. It has been moved inside the
@ -361,8 +446,10 @@ const J40Map = ({location}: IJ40Interface) => {
process.env.MAPBOX_STYLES_READ_TOKEN ?
process.env.MAPBOX_STYLES_READ_TOKEN : ''}
// ****** Map state props: ******
// http://visgl.github.io/react-map-gl/docs/api-reference/interactive-map#map-state
/**
* ****** Map state props: ******
* https://github.com/visgl/react-map-gl/blob/6.1-release/docs/api-reference/interactive-map.md#map-state
*/
{...viewport}
mapStyle={process.env.MAPBOX_STYLES_READ_TOKEN ? mapBoxBaseLayer : getOSBaseMap(censusSelected)}
width="100%"
@ -371,9 +458,10 @@ const J40Map = ({location}: IJ40Interface) => {
height="100%"
mapOptions={{hash: true}}
// ****** Interaction option props: ******
// http://visgl.github.io/react-map-gl/docs/api-reference/interactive-map#interaction-options
/**
* ****** Interaction options: ******
* https://github.com/visgl/react-map-gl/blob/6.1-release/docs/api-reference/interactive-map.md#interaction-options
*/
maxZoom={constants.GLOBAL_MAX_ZOOM}
minZoom={constants.GLOBAL_MIN_ZOOM}
dragRotate={false}
@ -389,10 +477,17 @@ const J40Map = ({location}: IJ40Interface) => {
]
}
/**
* ****** Callback props: ******
* https://github.com/visgl/react-map-gl/blob/6.1-release/docs/api-reference/interactive-map.md#callbacks
*/
// eslint-disable-next-line max-len
onViewportChange={(viewState: ViewportProps) => onViewportChange(viewState)}
// ****** Callback props: ******
// http://visgl.github.io/react-map-gl/docs/api-reference/interactive-map#callbacks
onViewportChange={setViewport}
// Detect and set the state variable is map is dragging
onInteractionStateChange={(interactionState: { isDragging: boolean; }) => {
interactionState.isDragging ? setIsMapBeingDragged(true) : setIsMapBeingDragged(false);
}}
onClick={onClick}
onLoad={onLoad}
onTransitionStart={onTransitionStart}

View file

@ -301,6 +301,57 @@ export const SELECTED_FEATURE_BORDER_WIDTH = 5.0;
export const ALAKSA_POINTS_STROKE_WIDTH = 1.0;
// Bounds - these bounds can be obtained by using the getCurrentMapBoundingBox() function in the map
/**
* ### Grab the lat/lon for the bounds of the map ###
*
* Alaska's coordinates:
* - latitude: 63.27827735223102
* - longitude: -162.3947525
*
* Hawaii's coordinates:
* - latitude: 20.657020350316177
* - longitude: -157.696737
*
* Mariana Islands coordinates:
* - latitude: 16.900917105280538
* - longitude: 145.47207349999974
*
* Guam's coordinates:
* - latitude: 14.366555855360668
* - longitude: 144.85327381019735
*
* USVI's coordinates:
* - latitude: 18.2017745170224
* - longitude: -64.77448172638938
*
* Maine's coordinates:
* - latitude: 42.52750243984539
* - longitude: -72.05693075466074
*
* ### Group the above by Lat/Lon ###
*
* - latitude: 63.27827735223102
* - latitude: 20.657020350316177
* - latitude: 16.900917105280538
* - latitude: 14.366555855360668
* - latitude: 18.2017745170224
* - latitude: 42.52750243984539
*
* - longitude: -162.3947525
* - longitude: -157.696737
* - longitude: 145.47207349999974
* - longitude: 144.85327381019735
* - longitude: -64.77448172638938
* - longitude: -72.05693075466074
*
* Set min/max lat/lon:
*/
export const MAP_BOUND_MIN_LATITUDE = 13;
export const MAP_BOUND_MAX_LATITUDE = 65;
export const MAP_BOUND_MIN_LONGITUDE = 150;
export const MAP_BOUND_MAX_LONGITUDE = -65;
export const GLOBAL_MAX_BOUNDS: LngLatBoundsLike = [
[-180.118306, 5.499550],
[-65.0, 83.162102],