@vis.gl/react-google-maps
Version:
React components and hooks for the Google Maps JavaScript API
94 lines (77 loc) • 2.38 kB
text/typescript
import {RefObject, useEffect, useRef} from 'react';
import {useForceUpdate} from '../../hooks/use-force-update';
/**
* Represents the 3D camera state with all position and orientation parameters.
*/
export type CameraState3D = {
center: google.maps.LatLngAltitudeLiteral;
range: number;
heading: number;
tilt: number;
roll: number;
};
export type CameraStateRef3D = RefObject<CameraState3D>;
const DEFAULT_CAMERA_STATE: CameraState3D = {
center: {lat: 0, lng: 0, altitude: 0},
range: 0,
heading: 0,
tilt: 0,
roll: 0
};
/**
* Camera property names that correspond to gmp-*change events.
*/
const CAMERA_PROPS = ['center', 'range', 'heading', 'tilt', 'roll'] as const;
type CameraProp = (typeof CAMERA_PROPS)[number];
/**
* Updates the camera state ref with values from the map element.
*/
function updateCameraState(
map3d: google.maps.maps3d.Map3DElement,
ref: CameraStateRef3D,
prop: CameraProp
) {
const value = map3d[prop];
if (value == null) return;
if (prop === 'center') {
// The center property returns a LatLngAltitude object, convert to literal
const center = value as google.maps.LatLngAltitude;
ref.current.center = center.toJSON
? center.toJSON()
: (center as google.maps.LatLngAltitudeLiteral);
} else {
ref.current[prop] = value as number;
}
}
/**
* Creates a mutable ref object to track the last known state of the 3D map camera.
* This is used in `useMap3DCameraParams` to reduce stuttering by avoiding updates
* of the map camera with values that have already been processed.
*
* @internal
*/
export function useTrackedCameraStateRef3D(
map3d: google.maps.maps3d.Map3DElement | null
): CameraStateRef3D {
const forceUpdate = useForceUpdate();
const ref = useRef<CameraState3D>({...DEFAULT_CAMERA_STATE});
useEffect(() => {
if (!map3d) return;
const listeners: (() => void)[] = [];
for (const prop of CAMERA_PROPS) {
const eventName = `gmp-${prop}change`;
const handler = () => {
updateCameraState(map3d, ref, prop);
forceUpdate();
};
map3d.addEventListener(eventName, handler);
listeners.push(() => map3d.removeEventListener(eventName, handler));
}
return () => {
for (const removeListener of listeners) {
removeListener();
}
};
}, [map3d, forceUpdate]);
return ref;
}