diff --git a/android/src/main/java/com/luggmaps/LuggMapView.kt b/android/src/main/java/com/luggmaps/LuggMapView.kt
index ece4cb9..82a133c 100644
--- a/android/src/main/java/com/luggmaps/LuggMapView.kt
+++ b/android/src/main/java/com/luggmaps/LuggMapView.kt
@@ -220,9 +220,15 @@ class LuggMapView(private val reactContext: ThemedReactContext) :
provider?.setTheme(value)
}
- fun setEdgeInsets(top: Int, left: Int, bottom: Int, right: Int) {
+ fun setEdgeInsets(
+ top: Int,
+ left: Int,
+ bottom: Int,
+ right: Int,
+ duration: Int = 0
+ ) {
edgeInsets = EdgeInsets(top, left, bottom, right)
- provider?.setEdgeInsets(edgeInsets)
+ provider?.setEdgeInsets(edgeInsets, duration)
}
// endregion
diff --git a/android/src/main/java/com/luggmaps/LuggMapViewManager.kt b/android/src/main/java/com/luggmaps/LuggMapViewManager.kt
index d38d7e7..2fd966e 100644
--- a/android/src/main/java/com/luggmaps/LuggMapViewManager.kt
+++ b/android/src/main/java/com/luggmaps/LuggMapViewManager.kt
@@ -159,6 +159,23 @@ class LuggMapViewManager :
view.moveCamera(latitude, longitude, zoom, duration.toInt())
}
+ override fun setEdgeInsets(
+ view: LuggMapView,
+ top: Double,
+ left: Double,
+ bottom: Double,
+ right: Double,
+ duration: Double
+ ) {
+ view.setEdgeInsets(
+ top.toFloat().dpToPx().toInt(),
+ left.toFloat().dpToPx().toInt(),
+ bottom.toFloat().dpToPx().toInt(),
+ right.toFloat().dpToPx().toInt(),
+ duration.toInt()
+ )
+ }
+
override fun fitCoordinates(
view: LuggMapView,
coordinates: ReadableArray?,
diff --git a/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt b/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt
index f38cc5b..b043b76 100644
--- a/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt
+++ b/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt
@@ -209,6 +209,19 @@ class GoogleMapProvider(private val context: Context) :
}
override fun setEdgeInsets(edgeInsets: EdgeInsets) {
+ val oldInsets = this.edgeInsets
+ this.edgeInsets = edgeInsets
+ applyEdgeInsets()
+
+ val map = googleMap
+ if (map != null && oldInsets != edgeInsets) {
+ mapView?.post {
+ setEdgeInsets(edgeInsets, 0)
+ }
+ }
+ }
+
+ override fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int) {
val map = googleMap
val oldInsets = this.edgeInsets
this.edgeInsets = edgeInsets
@@ -216,7 +229,11 @@ class GoogleMapProvider(private val context: Context) :
if (map != null && oldInsets != edgeInsets) {
val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
applyEdgeInsets()
- map.moveCamera(cameraUpdate)
+ when {
+ duration < 0 -> map.animateCamera(cameraUpdate)
+ duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
+ else -> map.moveCamera(cameraUpdate)
+ }
} else {
applyEdgeInsets()
}
diff --git a/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt b/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt
index 553acad..ce9ff85 100644
--- a/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt
+++ b/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt
@@ -30,6 +30,7 @@ interface MapProvider {
fun setMinZoom(zoom: Double)
fun setMaxZoom(zoom: Double)
fun setEdgeInsets(edgeInsets: EdgeInsets)
+ fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int)
// Children
fun addMarkerView(markerView: LuggMarkerView)
diff --git a/docs/MAPVIEW.md b/docs/MAPVIEW.md
index e761669..9f573cb 100644
--- a/docs/MAPVIEW.md
+++ b/docs/MAPVIEW.md
@@ -56,7 +56,13 @@ mapRef.current?.fitCoordinates(
{ latitude: 37.7749, longitude: -122.4194 },
{ latitude: 37.8049, longitude: -122.4094 },
],
- { edgeInsets: { top: 50, left: 50, bottom: 50, right: 50 }, duration: 500 }
+ { padding: { top: 50, left: 50, bottom: 50, right: 50 }, duration: 500 }
+);
+
+// Set edge insets with animation
+mapRef.current?.setEdgeInsets(
+ { top: 0, left: 0, bottom: 200, right: 0 },
+ { duration: 300 }
);
```
@@ -81,11 +87,23 @@ Fit multiple coordinates in the visible map area.
fitCoordinates(coordinates: Coordinate[], options?: FitCoordinatesOptions): void
interface FitCoordinatesOptions {
- edgeInsets?: EdgeInsets;
+ padding?: EdgeInsets;
duration?: number; // milliseconds, -1 for default
}
```
+### setEdgeInsets
+
+Programmatically update the map's edge insets with optional animation.
+
+```ts
+setEdgeInsets(edgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions): void
+
+interface SetEdgeInsetsOptions {
+ duration?: number; // milliseconds, -1 for default animation, 0 for instant
+}
+```
+
## Events
### onCameraMove
diff --git a/docs/POLYLINE.md b/docs/POLYLINE.md
index 9aaa734..a0a3f0a 100644
--- a/docs/POLYLINE.md
+++ b/docs/POLYLINE.md
@@ -38,6 +38,29 @@ import { MapView, Polyline } from '@lugg/maps';
| `coordinates` | `Coordinate[]` | **required** | Array of coordinates |
| `strokeWidth` | `number` | - | Line width in points |
| `strokeColors` | `ColorValue[]` | - | Gradient colors along the line |
+| `animated` | `boolean` | `false` | Animate the polyline with a snake effect |
+| `animatedOptions` | `PolylineAnimatedOptions` | - | Animation configuration options |
+| `zIndex` | `number` | - | Z-index for layering polylines |
+
+## Animated Options
+
+```ts
+interface PolylineAnimatedOptions {
+ duration?: number; // milliseconds, default 2150
+ easing?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'; // default 'linear'
+ trailLength?: number; // 0-1, default 1.0 (full snake effect)
+ delay?: number; // milliseconds, default 0
+}
+```
+
+```tsx
+
+```
## Gradient Colors
diff --git a/example/bare/ios/Podfile.lock b/example/bare/ios/Podfile.lock
index 96db141..0c75b36 100644
--- a/example/bare/ios/Podfile.lock
+++ b/example/bare/ios/Podfile.lock
@@ -11,7 +11,7 @@ PODS:
- hermes-engine (0.14.0):
- hermes-engine/Pre-built (= 0.14.0)
- hermes-engine/Pre-built (0.14.0)
- - LuggMaps (0.2.0-alpha.24):
+ - LuggMaps (0.2.0-alpha.27):
- boost
- DoubleConversion
- fast_float
@@ -3050,7 +3050,7 @@ SPEC CHECKSUMS:
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
GoogleMaps: 0608099d4870cac8754bdba9b6953db543432438
hermes-engine: 3515eff1a2de44b79dfa94a03d1adeed40f0dafe
- LuggMaps: decd48fedf38073fcd53a74394624d5eaa763b2b
+ LuggMaps: 52b43077d9f8ca87c7c9c22771aa61fd93df5ae3
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
RCTDeprecation: 2b70c6e3abe00396cefd8913efbf6a2db01a2b36
RCTRequired: f3540eee8094231581d40c5c6d41b0f170237a81
diff --git a/example/shared/src/Home.tsx b/example/shared/src/Home.tsx
index 9d2c30f..66c1e74 100644
--- a/example/shared/src/Home.tsx
+++ b/example/shared/src/Home.tsx
@@ -13,15 +13,15 @@ import {
type CameraEventPayload,
} from '@lugg/maps';
import {
- type TrueSheet,
+ TrueSheet,
TrueSheetProvider,
+ type DetentChangeEvent,
} from '@lodev09/react-native-true-sheet';
import {
ReanimatedTrueSheet,
ReanimatedTrueSheetProvider,
useReanimatedTrueSheet,
} from '@lodev09/react-native-true-sheet/reanimated';
-import { useDerivedValue } from 'react-native-reanimated';
import { Button, Map } from './components';
import { randomFrom, randomLetter } from './utils';
@@ -33,35 +33,71 @@ import {
} from './markers';
import { useLocationPermission } from './useLocationPermission';
+const bottomEdgeInsets = (bottom: number) => ({
+ top: 0,
+ left: 0,
+ bottom,
+ right: 0,
+});
+
+export function Home() {
+ const apiKey = process.env.GOOGLE_MAPS_API_KEY;
+
+ return (
+
+
+
+
+
+
+
+ );
+}
+
function HomeContent() {
const mapRef = useRef(null);
const sheetRef = useRef(null);
const { height: screenHeight } = useWindowDimensions();
const locationPermission = useLocationPermission();
+ const { animatedPosition } = useReanimatedTrueSheet();
const [provider, setProvider] = useState('apple');
const [showMap, setShowMap] = useState(true);
const [markers, setMarkers] = useState(INITIAL_MARKERS);
const [cameraPosition, setCameraPosition] = useState();
const [isIdle, setIsIdle] = useState(true);
- const { animatedPosition } = useReanimatedTrueSheet();
+ const getSheetBottom = useCallback(
+ (event: DetentChangeEvent) => screenHeight - event.nativeEvent.position,
+ [screenHeight]
+ );
- const animatedEdgeInsetsBottom = useDerivedValue(
- () => screenHeight - animatedPosition.value
+ const handleMapReady = useCallback(() => {
+ const bottom = screenHeight - animatedPosition.value;
+ if (bottom > 0) {
+ mapRef.current?.setEdgeInsets(bottomEdgeInsets(bottom));
+ }
+ }, [screenHeight, animatedPosition]);
+
+ const handleSheetPresent = useCallback(
+ (event: DetentChangeEvent) => {
+ const bottom = getSheetBottom(event);
+ mapRef.current?.setEdgeInsets(bottomEdgeInsets(bottom));
+ },
+ [getSheetBottom]
);
- const handleCameraMove = useCallback(
- (event: { nativeEvent: CameraEventPayload }) => {
- setCameraPosition(event.nativeEvent);
- setIsIdle(false);
+ const handleDetentChange = useCallback(
+ (event: DetentChangeEvent) => {
+ const bottom = getSheetBottom(event);
+ mapRef.current?.setEdgeInsets(bottomEdgeInsets(bottom));
},
- []
+ [getSheetBottom]
);
- const handleCameraIdle = useCallback(
- (event: { nativeEvent: CameraEventPayload }) => {
+ const handleCameraEvent = useCallback(
+ (event: { nativeEvent: CameraEventPayload }, idle: boolean) => {
setCameraPosition(event.nativeEvent);
- setIsIdle(true);
+ setIsIdle(idle);
},
[]
);
@@ -103,7 +139,7 @@ function HomeContent() {
const fitAllMarkers = () => {
const coordinates = markers.map((m) => m.coordinate);
mapRef.current?.fitCoordinates(coordinates, {
- edgeInsets: {
+ padding: {
top: 60,
left: 40,
right: 40,
@@ -112,93 +148,82 @@ function HomeContent() {
});
};
- const apiKey = process.env.GOOGLE_MAPS_API_KEY;
-
return (
-
-
-
- {showMap && (
-
+
+ {showMap && (
+
-
- );
-}
-
-export function Home() {
- return (
-
-
-
+
+
);
}
diff --git a/example/shared/src/components/Map.tsx b/example/shared/src/components/Map.tsx
index 976d060..1e55229 100644
--- a/example/shared/src/components/Map.tsx
+++ b/example/shared/src/components/Map.tsx
@@ -1,5 +1,5 @@
import { forwardRef, useMemo, useState } from 'react';
-import { Platform, StyleSheet, View } from 'react-native';
+import { StyleSheet, View, useWindowDimensions } from 'react-native';
import {
MapView,
Marker,
@@ -8,30 +8,24 @@ import {
} from '@lugg/maps';
import type { NativeSyntheticEvent } from 'react-native';
import Animated, {
- useAnimatedProps,
- useAnimatedReaction,
useAnimatedStyle,
- useDerivedValue,
type SharedValue,
} from 'react-native-reanimated';
-const isWeb = Platform.OS === 'web';
-const AnimatedMapView = isWeb
- ? MapView
- : Animated.createAnimatedComponent(MapView);
-
+import { CrewMarker } from './CrewMarker';
import { MarkerIcon } from './MarkerIcon';
import { MarkerText } from './MarkerText';
import { MarkerImage } from './MarkerImage';
-import { CrewMarker } from './CrewMarker';
import type { MarkerData } from './index';
import { Route, smoothCoordinates } from './Route';
interface MapProps extends MapViewProps {
markers: MarkerData[];
- animatedEdgeInsetsBottom?: SharedValue;
+ animatedPosition?: SharedValue;
}
+const INITIAL_ZOOM = 14;
+
const renderMarker = (marker: MarkerData) => {
const {
id,
@@ -71,7 +65,9 @@ const renderMarker = (marker: MarkerData) => {
case 'custom':
return (
-
+
);
default:
@@ -87,20 +83,19 @@ const renderMarker = (marker: MarkerData) => {
}
};
-const INITIAL_ZOOM = 14;
-
export const Map = forwardRef(
(
{
markers,
edgeInsets,
- animatedEdgeInsetsBottom,
+ animatedPosition,
onCameraIdle,
onCameraMove,
...props
},
ref
) => {
+ const { height: screenHeight } = useWindowDimensions();
const [zoom, setZoom] = useState(INITIAL_ZOOM);
const polylineCoordinates = useMemo(
() => markers.map((m) => m.coordinate),
@@ -111,29 +106,14 @@ export const Map = forwardRef(
[polylineCoordinates]
);
- const animatedEdgeInsets = useDerivedValue(() => ({
- top: edgeInsets?.top ?? 0,
- left: edgeInsets?.left ?? 0,
- right: edgeInsets?.right ?? 0,
- bottom: animatedEdgeInsetsBottom?.value ?? edgeInsets?.bottom ?? 0,
- }));
-
- const [webEdgeInsets, setWebEdgeInsets] = useState(edgeInsets);
-
- useAnimatedReaction(
- () => animatedEdgeInsets.value,
- (value) => {
- if (isWeb) setWebEdgeInsets(value);
- }
- );
-
- const animatedProps = useAnimatedProps(() => ({
- edgeInsets: animatedEdgeInsets.value,
- }));
-
- const centerPinStyle = useAnimatedStyle(() => ({
- transform: [{ translateY: -animatedEdgeInsets.value.bottom / 2 }],
- }));
+ const centerPinStyle = useAnimatedStyle(() => {
+ const bottom = animatedPosition
+ ? screenHeight - animatedPosition.value
+ : 0;
+ return {
+ transform: [{ translateY: -bottom / 2 }],
+ };
+ });
const handleCameraMove = (e: NativeSyntheticEvent) => {
onCameraMove?.(e);
@@ -146,14 +126,13 @@ export const Map = forwardRef(
return (
- (
text="LO"
color="#34A853"
/>
-
+
);
@@ -180,7 +159,6 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
- map: { flex: 1, alignItems: 'center', justifyContent: 'center' },
centerPin: {
backgroundColor: 'blue',
height: 20,
@@ -188,7 +166,6 @@ const styles = StyleSheet.create({
borderRadius: 10,
},
customMarker: {
- backgroundColor: 'gray',
height: 30,
width: 30,
borderRadius: 15,
diff --git a/ios/LuggMapView.mm b/ios/LuggMapView.mm
index 948b202..45cc83e 100644
--- a/ios/LuggMapView.mm
+++ b/ios/LuggMapView.mm
@@ -273,6 +273,18 @@ - (void)fitCoordinates:(NSArray *)coordinates
duration:duration];
}
+- (void)setEdgeInsets:(double)top
+ left:(double)left
+ bottom:(double)bottom
+ right:(double)right
+ duration:(double)duration {
+ UIEdgeInsets oldInsets = _edgeInsets;
+ _edgeInsets = UIEdgeInsetsMake(top, left, bottom, right);
+ [_provider setEdgeInsets:_edgeInsets
+ oldEdgeInsets:oldInsets
+ duration:duration];
+}
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
RCTLuggMapViewHandleCommand(self, commandName, args);
}
diff --git a/ios/core/AppleMapProvider.mm b/ios/core/AppleMapProvider.mm
index de3aab2..e01d711 100644
--- a/ios/core/AppleMapProvider.mm
+++ b/ios/core/AppleMapProvider.mm
@@ -29,6 +29,13 @@ @implementation AppleMapProvider {
double _minZoom;
double _maxZoom;
NSMapTable, LuggPolylineView *> *_overlayToPolylineMap;
+
+ // Edge insets animation
+ CADisplayLink *_edgeInsetsDisplayLink;
+ UIEdgeInsets _edgeInsetsFrom;
+ UIEdgeInsets _edgeInsetsTo;
+ CFTimeInterval _edgeInsetsAnimationStart;
+ CFTimeInterval _edgeInsetsAnimationDuration;
}
@synthesize delegate = _delegate;
@@ -76,6 +83,7 @@ - (void)initializeMapInView:(UIView *)wrapperView
}
- (void)destroy {
+ [self stopEdgeInsetsAnimation];
[_mapView removeFromSuperview];
_mapView = nil;
_isMapReady = NO;
@@ -151,6 +159,55 @@ - (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
}
}
+- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
+ oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets
+ duration:(double)duration {
+ [self stopEdgeInsetsAnimation];
+
+ if (duration != 0 && _mapView) {
+ double actualDuration = duration < 0 ? 0.3 : duration / 1000.0;
+ _edgeInsetsFrom = oldEdgeInsets;
+ _edgeInsetsTo = edgeInsets;
+ _edgeInsetsAnimationDuration = actualDuration;
+ _edgeInsetsAnimationStart = CACurrentMediaTime();
+
+ _edgeInsetsDisplayLink = [CADisplayLink
+ displayLinkWithTarget:self
+ selector:@selector(edgeInsetsAnimationTick:)];
+ [_edgeInsetsDisplayLink addToRunLoop:[NSRunLoop mainRunLoop]
+ forMode:NSRunLoopCommonModes];
+ } else {
+ [self setEdgeInsets:edgeInsets oldEdgeInsets:oldEdgeInsets];
+ }
+}
+
+- (void)edgeInsetsAnimationTick:(CADisplayLink *)displayLink {
+ CFTimeInterval elapsed = CACurrentMediaTime() - _edgeInsetsAnimationStart;
+ CGFloat progress = MIN(elapsed / _edgeInsetsAnimationDuration, 1.0);
+
+ // Ease out cubic
+ CGFloat t = 1.0 - (1.0 - progress) * (1.0 - progress) * (1.0 - progress);
+
+ UIEdgeInsets current = UIEdgeInsetsMake(
+ _edgeInsetsFrom.top + (_edgeInsetsTo.top - _edgeInsetsFrom.top) * t,
+ _edgeInsetsFrom.left + (_edgeInsetsTo.left - _edgeInsetsFrom.left) * t,
+ _edgeInsetsFrom.bottom +
+ (_edgeInsetsTo.bottom - _edgeInsetsFrom.bottom) * t,
+ _edgeInsetsFrom.right +
+ (_edgeInsetsTo.right - _edgeInsetsFrom.right) * t);
+
+ [self setEdgeInsets:current oldEdgeInsets:_mapView.layoutMargins];
+
+ if (progress >= 1.0) {
+ [self stopEdgeInsetsAnimation];
+ }
+}
+
+- (void)stopEdgeInsetsAnimation {
+ [_edgeInsetsDisplayLink invalidate];
+ _edgeInsetsDisplayLink = nil;
+}
+
- (CLLocationDistance)cameraDistanceForZoomLevel:(double)zoomLevel {
return 128000000.0 / pow(2, zoomLevel);
}
diff --git a/ios/core/GoogleMapProvider.mm b/ios/core/GoogleMapProvider.mm
index ac04992..a0e19fe 100644
--- a/ios/core/GoogleMapProvider.mm
+++ b/ios/core/GoogleMapProvider.mm
@@ -19,6 +19,13 @@ @implementation GoogleMapProvider {
NSMutableArray *_pendingMarkerViews;
NSMutableArray *_pendingPolylineViews;
NSMapTable *_polylineAnimators;
+
+ // Edge insets animation
+ CADisplayLink *_edgeInsetsDisplayLink;
+ UIEdgeInsets _edgeInsetsFrom;
+ UIEdgeInsets _edgeInsetsTo;
+ CFTimeInterval _edgeInsetsAnimationStart;
+ CFTimeInterval _edgeInsetsAnimationDuration;
}
@synthesize delegate = _delegate;
@@ -86,6 +93,7 @@ - (void)initializeMapInView:(UIView *)wrapperView
}
- (void)destroy {
+ [self stopEdgeInsetsAnimation];
[_pendingMarkerViews removeAllObjects];
[_pendingPolylineViews removeAllObjects];
[_polylineAnimators removeAllObjects];
@@ -155,10 +163,61 @@ - (void)setMaxZoom:(double)maxZoom {
- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets {
+ [self setEdgeInsets:edgeInsets oldEdgeInsets:oldEdgeInsets duration:0];
+}
+
+- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
+ oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets
+ duration:(double)duration {
if (UIEdgeInsetsEqualToEdgeInsets(_edgeInsets, edgeInsets))
return;
- _edgeInsets = edgeInsets;
- _mapView.padding = edgeInsets;
+
+ [self stopEdgeInsetsAnimation];
+
+ if (duration != 0 && _mapView) {
+ double actualDuration = duration < 0 ? 0.3 : duration / 1000.0;
+ _edgeInsetsFrom = _edgeInsets;
+ _edgeInsetsTo = edgeInsets;
+ _edgeInsets = edgeInsets;
+ _edgeInsetsAnimationDuration = actualDuration;
+ _edgeInsetsAnimationStart = CACurrentMediaTime();
+
+ _edgeInsetsDisplayLink = [CADisplayLink
+ displayLinkWithTarget:self
+ selector:@selector(edgeInsetsAnimationTick:)];
+ [_edgeInsetsDisplayLink addToRunLoop:[NSRunLoop mainRunLoop]
+ forMode:NSRunLoopCommonModes];
+ } else {
+ _edgeInsets = edgeInsets;
+ _mapView.padding = edgeInsets;
+ }
+}
+
+- (void)edgeInsetsAnimationTick:(CADisplayLink *)displayLink {
+ CFTimeInterval elapsed = CACurrentMediaTime() - _edgeInsetsAnimationStart;
+ CGFloat progress = MIN(elapsed / _edgeInsetsAnimationDuration, 1.0);
+
+ // Ease out cubic
+ CGFloat t = 1.0 - (1.0 - progress) * (1.0 - progress) * (1.0 - progress);
+
+ UIEdgeInsets current = UIEdgeInsetsMake(
+ _edgeInsetsFrom.top + (_edgeInsetsTo.top - _edgeInsetsFrom.top) * t,
+ _edgeInsetsFrom.left + (_edgeInsetsTo.left - _edgeInsetsFrom.left) * t,
+ _edgeInsetsFrom.bottom +
+ (_edgeInsetsTo.bottom - _edgeInsetsFrom.bottom) * t,
+ _edgeInsetsFrom.right +
+ (_edgeInsetsTo.right - _edgeInsetsFrom.right) * t);
+
+ _mapView.padding = current;
+
+ if (progress >= 1.0) {
+ [self stopEdgeInsetsAnimation];
+ }
+}
+
+- (void)stopEdgeInsetsAnimation {
+ [_edgeInsetsDisplayLink invalidate];
+ _edgeInsetsDisplayLink = nil;
}
#pragma mark - GMSMapViewDelegate
diff --git a/ios/core/MapProviderDelegate.h b/ios/core/MapProviderDelegate.h
index 9c52b26..9e571e0 100644
--- a/ios/core/MapProviderDelegate.h
+++ b/ios/core/MapProviderDelegate.h
@@ -39,6 +39,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setMaxZoom:(double)maxZoom;
- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets;
+- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
+ oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets
+ duration:(double)duration;
// Children
- (void)addMarkerView:(LuggMarkerView *)markerView;
diff --git a/src/MapView.tsx b/src/MapView.tsx
index 0d250c5..4120657 100644
--- a/src/MapView.tsx
+++ b/src/MapView.tsx
@@ -9,8 +9,9 @@ import type {
MapViewRef,
MoveCameraOptions,
FitCoordinatesOptions,
+ SetEdgeInsetsOptions,
} from './MapView.types';
-import type { Coordinate } from './types';
+import type { Coordinate, EdgeInsets } from './types';
export class MapView
extends React.Component
@@ -47,8 +48,8 @@ export class MapView
const first = coordinates[0];
if (!ref || !first) return;
- const { edgeInsets, duration = -1 } = options ?? {};
- const { top = 0, left = 0, bottom = 0, right = 0 } = edgeInsets ?? {};
+ const { padding, duration = -1 } = options ?? {};
+ const { top = 0, left = 0, bottom = 0, right = 0 } = padding ?? {};
if (coordinates.length === 1) {
const zoom = this.props.initialZoom ?? 10;
@@ -67,6 +68,15 @@ export class MapView
);
}
+ setEdgeInsets(edgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions) {
+ const ref = this.nativeRef.current;
+ if (!ref) return;
+
+ const { top = 0, left = 0, bottom = 0, right = 0 } = edgeInsets;
+ const { duration = -1 } = options ?? {};
+ Commands.setEdgeInsets(ref, top, left, bottom, right, duration);
+ }
+
render() {
const {
provider,
diff --git a/src/MapView.types.ts b/src/MapView.types.ts
index fb4089b..9d8920c 100644
--- a/src/MapView.types.ts
+++ b/src/MapView.types.ts
@@ -14,7 +14,14 @@ export interface MoveCameraOptions {
* Options for fitting coordinates in view
*/
export interface FitCoordinatesOptions {
- edgeInsets?: EdgeInsets;
+ padding?: EdgeInsets;
+ duration?: number;
+}
+
+/**
+ * Options for setting edge insets
+ */
+export interface SetEdgeInsetsOptions {
duration?: number;
}
@@ -27,6 +34,7 @@ export interface MapViewRef {
coordinates: Coordinate[],
options?: FitCoordinatesOptions
): void;
+ setEdgeInsets(edgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions): void;
}
/**
diff --git a/src/MapView.web.tsx b/src/MapView.web.tsx
index 59507e9..dd13e2d 100644
--- a/src/MapView.web.tsx
+++ b/src/MapView.web.tsx
@@ -25,9 +25,10 @@ import type {
MapViewRef,
MoveCameraOptions,
FitCoordinatesOptions,
+ SetEdgeInsetsOptions,
CameraEventPayload,
} from './MapView.types';
-import type { Coordinate } from './types';
+import type { Coordinate, EdgeInsets } from './types';
const createSyntheticEvent = (nativeEvent: T): NativeSyntheticEvent =>
({
@@ -126,15 +127,9 @@ export const MapView = forwardRef(function MapView(
const prevEdgeInsets = useRef(edgeInsets);
const offsetCenter = useCallback(
- (
- coord: Coordinate,
- zoom: number,
- edgeInsetsOverride?: typeof edgeInsets,
- reverse = false
- ) => {
- const p = edgeInsetsOverride ?? edgeInsets;
- const div = map?.getDiv();
- if (!p || !div) {
+ (coord: Coordinate, zoom: number, insets?: EdgeInsets, reverse = false) => {
+ const p = insets ?? prevEdgeInsets.current;
+ if (!p) {
return { lat: coord.latitude, lng: coord.longitude };
}
@@ -157,7 +152,41 @@ export const MapView = forwardRef(function MapView(
return { lat, lng };
},
- [map, edgeInsets]
+ []
+ );
+
+ const applyEdgeInsets = useCallback(
+ (newEdgeInsets: EdgeInsets, duration?: number) => {
+ if (!map) return;
+
+ const prev = prevEdgeInsets.current;
+ const center = map.getCenter();
+ const zoom = map.getZoom() ?? initialZoom;
+
+ if (center) {
+ const logicalCenter = offsetCenter(
+ { latitude: center.lat(), longitude: center.lng() },
+ zoom,
+ prev,
+ true
+ );
+ const newCenter = offsetCenter(
+ { latitude: logicalCenter.lat, longitude: logicalCenter.lng },
+ zoom,
+ newEdgeInsets,
+ false
+ );
+
+ if (duration === 0) {
+ map.moveCamera({ center: newCenter, zoom });
+ } else {
+ map.panTo(newCenter);
+ }
+ }
+
+ prevEdgeInsets.current = newEdgeInsets;
+ },
+ [map, initialZoom, offsetCenter]
);
useImperativeHandle(
@@ -190,7 +219,7 @@ export const MapView = forwardRef(function MapView(
const first = coordinates[0];
if (!map || !first) return;
- const { edgeInsets: fitEdgeInsets, duration = -1 } = options ?? {};
+ const { padding: fitPadding, duration = -1 } = options ?? {};
if (coordinates.length === 1) {
this.moveCamera(first, { zoom: initialZoom, duration });
@@ -202,15 +231,20 @@ export const MapView = forwardRef(function MapView(
bounds.extend({ lat: coord.latitude, lng: coord.longitude });
});
+ const ei = prevEdgeInsets.current;
map.fitBounds(bounds, {
- top: (edgeInsets?.top ?? 0) + (fitEdgeInsets?.top ?? 0),
- left: (edgeInsets?.left ?? 0) + (fitEdgeInsets?.left ?? 0),
- bottom: (edgeInsets?.bottom ?? 0) + (fitEdgeInsets?.bottom ?? 0),
- right: (edgeInsets?.right ?? 0) + (fitEdgeInsets?.right ?? 0),
+ top: (ei?.top ?? 0) + (fitPadding?.top ?? 0),
+ left: (ei?.left ?? 0) + (fitPadding?.left ?? 0),
+ bottom: (ei?.bottom ?? 0) + (fitPadding?.bottom ?? 0),
+ right: (ei?.right ?? 0) + (fitPadding?.right ?? 0),
});
},
+
+ setEdgeInsets(newEdgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions) {
+ applyEdgeInsets(newEdgeInsets, options?.duration);
+ },
}),
- [map, initialZoom, edgeInsets, offsetCenter]
+ [map, initialZoom, offsetCenter, applyEdgeInsets]
);
useEffect(() => {
@@ -231,26 +265,9 @@ export const MapView = forwardRef(function MapView(
prev?.right !== edgeInsets.right;
if (changed) {
- const center = map.getCenter();
- const zoom = map.getZoom() ?? initialZoom;
- if (center) {
- const logicalCenter = offsetCenter(
- { latitude: center.lat(), longitude: center.lng() },
- zoom,
- prev,
- true
- );
- const newCenter = offsetCenter(
- { latitude: logicalCenter.lat, longitude: logicalCenter.lng },
- zoom,
- edgeInsets,
- false
- );
- map.panTo(newCenter);
- }
- prevEdgeInsets.current = edgeInsets;
+ applyEdgeInsets(edgeInsets);
}
- }, [map, edgeInsets, initialZoom, offsetCenter]);
+ }, [map, edgeInsets, applyEdgeInsets]);
const handleDragStart = () => {
setIsDragging(true);
diff --git a/src/fabric/LuggMapViewNativeComponent.ts b/src/fabric/LuggMapViewNativeComponent.ts
index a9ed208..f49a6ba 100644
--- a/src/fabric/LuggMapViewNativeComponent.ts
+++ b/src/fabric/LuggMapViewNativeComponent.ts
@@ -77,10 +77,18 @@ interface NativeCommands {
edgeInsetsRight: Double,
duration: Double
) => void;
+ setEdgeInsets: (
+ viewRef: React.ElementRef,
+ top: Double,
+ left: Double,
+ bottom: Double,
+ right: Double,
+ duration: Double
+ ) => void;
}
export const Commands = codegenNativeCommands({
- supportedCommands: ['moveCamera', 'fitCoordinates'],
+ supportedCommands: ['moveCamera', 'fitCoordinates', 'setEdgeInsets'],
});
export default codegenNativeComponent(
diff --git a/src/index.ts b/src/index.ts
index 416ad9e..d2ea408 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -14,6 +14,7 @@ export type {
MapViewRef,
MoveCameraOptions,
FitCoordinatesOptions,
+ SetEdgeInsetsOptions,
CameraEventPayload,
} from './MapView.types';
export type {