Implement camera controls and expose MapHandle API in MapView
This commit is contained in:
@@ -1,11 +1,27 @@
|
|||||||
import React from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { SafeAreaView, StyleSheet } from 'react-native';
|
import { SafeAreaView, StyleSheet, Button, View } from 'react-native';
|
||||||
import { MapView, Marker } from '@lynkedup/map-sdk';
|
import { MapView, Marker } from '@lynkedup/map-sdk';
|
||||||
|
import type { MapHandle } from '@lynkedup/map-sdk';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
const mapRef = useRef<MapHandle | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
|
<View style={styles.controls}>
|
||||||
|
<Button
|
||||||
|
title="Fly to SF"
|
||||||
|
onPress={() => mapRef.current?.flyTo({ latitude: 37.78825, longitude: -122.4324 })}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Fit Bounds"
|
||||||
|
onPress={() =>
|
||||||
|
mapRef.current?.fitBounds({ latitude: 37.809, longitude: -122.410 }, { latitude: 37.779, longitude: -122.450 })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
<MapView
|
<MapView
|
||||||
|
ref={mapRef}
|
||||||
style={styles.map}
|
style={styles.map}
|
||||||
initialRegion={{ latitude: 37.78825, longitude: -122.4324, latitudeDelta: 0.0922, longitudeDelta: 0.0421 }}
|
initialRegion={{ latitude: 37.78825, longitude: -122.4324, latitudeDelta: 0.0922, longitudeDelta: 0.0421 }}
|
||||||
>
|
>
|
||||||
@@ -17,5 +33,6 @@ export default function App() {
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: { flex: 1 },
|
container: { flex: 1 },
|
||||||
|
controls: { position: 'absolute', top: 40, left: 10, right: 10, zIndex: 1000, flexDirection: 'row', justifyContent: 'space-between' },
|
||||||
map: { flex: 1 }
|
map: { flex: 1 }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,5 +29,26 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Camera Controls 🔧
|
||||||
|
|
||||||
|
This package exposes a small imperative `MapHandle` API via `ref` on `MapView`. Use it to animate the camera or fit bounds.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const mapRef = useRef<MapHandle | null>(null);
|
||||||
|
mapRef.current?.flyTo({ latitude: 37.78825, longitude: -122.4324 });
|
||||||
|
mapRef.current?.fitBounds({ latitude: 37.809, longitude: -122.410 }, { latitude: 37.779, longitude: -122.450 });
|
||||||
|
```
|
||||||
|
|
||||||
|
Available methods:
|
||||||
|
|
||||||
|
- `animateToRegion(region, duration?)` — animate to a region.
|
||||||
|
- `animateCamera(camera, options?)` — animate using camera properties.
|
||||||
|
- `flyTo(coordinate, duration?)` — quick helper that animates to a small region around `coordinate`.
|
||||||
|
- `fitBounds(northEast, southWest, options?)` — fit the two coordinates with optional `edgePadding`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
- This package delegates to `react-native-maps` for platform implementations. Follow `react-native-maps` docs for iOS/Android setup (Google Maps API key, pods, manifest permissions).
|
- This package delegates to `react-native-maps` for platform implementations. Follow `react-native-maps` docs for iOS/Android setup (Google Maps API key, pods, manifest permissions).
|
||||||
|
|||||||
@@ -1,10 +1,78 @@
|
|||||||
import React from 'react';
|
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
import RNMapView, { MapViewProps as RNMapViewProps } from 'react-native-maps';
|
import RNMapView, { MapViewProps as RNMapViewProps, Camera, Region, LatLng } from 'react-native-maps';
|
||||||
|
|
||||||
export type MapProps = RNMapViewProps;
|
export type MapProps = RNMapViewProps;
|
||||||
|
|
||||||
const MapView: React.FC<MapProps> = (props) => {
|
export type MapHandle = {
|
||||||
return <RNMapView {...props}>{props.children}</RNMapView>;
|
animateToRegion: (region: Region, duration?: number) => void;
|
||||||
|
animateCamera: (camera: Partial<Camera>, options?: { duration?: number }) => void;
|
||||||
|
flyTo: (coordinate: LatLng, duration?: number) => void;
|
||||||
|
fitBounds: (
|
||||||
|
northEast: LatLng,
|
||||||
|
southWest: LatLng,
|
||||||
|
options?: { edgePadding?: { top: number; left: number; bottom: number; right: number }; animated?: boolean }
|
||||||
|
) => void;
|
||||||
|
getCamera?: () => Promise<Camera | undefined>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MapView = forwardRef<MapHandle, MapProps>((props, ref) => {
|
||||||
|
const mapRef = useRef<RNMapView | null>(null);
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
animateToRegion(region: Region, duration = 500) {
|
||||||
|
if (mapRef.current?.animateToRegion) {
|
||||||
|
mapRef.current.animateToRegion(region, duration);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animateCamera(camera: Partial<Camera>, options) {
|
||||||
|
if (mapRef.current?.animateCamera) {
|
||||||
|
mapRef.current.animateCamera(camera, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flyTo(coordinate: LatLng, duration = 800) {
|
||||||
|
if (mapRef.current?.animateToRegion) {
|
||||||
|
const region: Region = {
|
||||||
|
latitude: coordinate.latitude,
|
||||||
|
longitude: coordinate.longitude,
|
||||||
|
latitudeDelta: 0.01,
|
||||||
|
longitudeDelta: 0.01,
|
||||||
|
};
|
||||||
|
mapRef.current.animateToRegion(region, duration);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fitBounds(
|
||||||
|
northEast: LatLng,
|
||||||
|
southWest: LatLng,
|
||||||
|
options = { edgePadding: { top: 20, left: 20, bottom: 20, right: 20 }, animated: true }
|
||||||
|
) {
|
||||||
|
if (mapRef.current?.fitToCoordinates) {
|
||||||
|
mapRef.current.fitToCoordinates([northEast, southWest], {
|
||||||
|
edgePadding: options.edgePadding,
|
||||||
|
animated: options.animated,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getCamera() {
|
||||||
|
if (mapRef.current?.getCamera) {
|
||||||
|
try {
|
||||||
|
return await mapRef.current.getCamera();
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RNMapView ref={mapRef} {...props}>
|
||||||
|
{props.children}
|
||||||
|
</RNMapView>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default MapView;
|
export default MapView;
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export { default as MapView } from './MapView';
|
export { default as MapView } from './MapView';
|
||||||
|
export type { MapHandle } from './MapView';
|
||||||
export { Marker, Polyline, PROVIDER_GOOGLE } from 'react-native-maps';
|
export { Marker, Polyline, PROVIDER_GOOGLE } from 'react-native-maps';
|
||||||
|
|||||||
Reference in New Issue
Block a user