From 43fc6a0d0cf1eb1f1811f3309b2fa1b6e22ce441 Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Fri, 9 May 2025 12:47:21 +0200 Subject: [PATCH 01/10] [android] add easeTo, moveBy and scaleBy using screen coordinates --- .../components/camera/RNMBXCameraModule.kt | 96 ++++++++++++++++- .../rnmbx/NativeRNMBXCameraModuleSpec.java | 12 +++ docs/Camera.md | 32 +++++- docs/docs.json | 27 +++++ src/components/Camera.tsx | 101 ++++++++++++++++++ src/specs/NativeRNMBXCameraModule.ts | 22 ++++ src/web/components/Camera.tsx | 2 +- 7 files changed, 288 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt index 0c9f710ee0..2d484cd0a0 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt @@ -1,13 +1,21 @@ package com.rnmapbox.rnmbx.components.camera -import com.facebook.react.bridge.Callback +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.LinearInterpolator import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.WritableNativeMap +import com.mapbox.maps.ScreenCoordinate +import com.mapbox.maps.extension.style.expressions.dsl.generated.zoom +import com.mapbox.maps.plugin.animation.MapAnimationOptions +import com.mapbox.maps.plugin.animation.easeTo +import com.mapbox.maps.plugin.animation.moveBy +import com.mapbox.maps.plugin.animation.scaleBy +import com.mapbox.maps.toCameraOptions import com.rnmapbox.rnmbx.NativeRNMBXCameraModuleSpec +import com.rnmapbox.rnmbx.components.camera.constants.CameraMode import com.rnmapbox.rnmbx.components.mapview.CommandResponse import com.rnmapbox.rnmbx.utils.ViewRefTag import com.rnmapbox.rnmbx.utils.ViewTagResolver @@ -49,4 +57,88 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V promise.resolve(null) } } + + private fun getAnimationOptions( + animationMode: Double?, + animationDuration: Double? + ): MapAnimationOptions { + return MapAnimationOptions.Builder() + .apply { + when (animationMode?.toInt()) { + CameraMode.LINEAR -> interpolator(LinearInterpolator()) + CameraMode.EASE -> interpolator(AccelerateDecelerateInterpolator()) + } + animationDuration?.let { duration -> + duration(duration.toLong()) + } + } + .build() + } + + override fun moveBy( + viewRef: ViewRefTag?, + x: Double, + y: Double, + animationMode: Double?, + animationDuration: Double?, + promise: Promise + ) { + withViewportOnUIThread(viewRef, promise) { + it.mapboxMap?.let { map -> + val animationOptions = getAnimationOptions(animationMode, animationDuration) + map.moveBy(ScreenCoordinate(x, y), animationOptions) + + promise.resolve(null) + } + } + } + + override fun easeTo( + viewRef: ViewRefTag?, + x: Double, + y: Double, + animationDuration: Double?, + scaleFactor: Double?, + promise: Promise + ) { + withViewportOnUIThread(viewRef, promise) { + it.mapboxMap?.let { map -> + val cameraOptions = + map.cameraState.toCameraOptions().toBuilder().apply { + scaleFactor?.let { scale -> + zoom(map.cameraState.zoom + scale) + } + center(map.coordinateForPixel(ScreenCoordinate(x, y))) + }.build() + + val animationOptions = + getAnimationOptions(CameraMode.EASE.toDouble(), animationDuration) + + map.easeTo(cameraOptions, animationOptions) + + promise.resolve(null) + } + } + } + + override fun scaleBy( + viewRef: ViewRefTag?, + x: Double, + y: Double, + animationMode: Double?, + animationDuration: Double?, + scaleFactor: Double?, + promise: Promise + ) { + withViewportOnUIThread(viewRef, promise) { + it.mapboxMap?.let { map -> + val animationOptions = + getAnimationOptions(animationMode, animationDuration) + + map.scaleBy(scaleFactor ?: 1.0, ScreenCoordinate(x, y), animationOptions) + + promise.resolve(null) + } + } + } } \ No newline at end of file diff --git a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java index 4cdf8937e0..fd9ad09af1 100644 --- a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java +++ b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java @@ -38,4 +38,16 @@ public NativeRNMBXCameraModuleSpec(ReactApplicationContext reactContext) { @ReactMethod @DoNotStrip public abstract void updateCameraStop(@Nullable Double viewRef, ReadableMap stop, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void easeTo(@Nullable Double viewRef, double x, double y, @Nullable Double animationDuration, @Nullable Double scaleFactor, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void moveBy(@Nullable Double viewRef, double x, double y, @Nullable Double animationMode, @Nullable Double animationDuration, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void scaleBy(@Nullable Double viewRef, double x, double y, @Nullable Double animationMode, @Nullable Double animationDuration, @Nullable Double scaleFactor, Promise promise); } diff --git a/docs/Camera.md b/docs/Camera.md index 8c8a017636..bbad827867 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -347,4 +347,34 @@ camera.zoomTo(16, 100); ``` -[Fit](../examples/Camera/Fit) +[Fit](../examples/Camera/Fit)### easeTo() + +Ease the map camera to a given camera options and animation options + +#### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | + + + +### moveBy() + +Move the map by a given screen coordinate with optional animation. + +#### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | + + + +### scaleBy() + +Scale the map by with optional animation. + +#### arguments +| Name | Type | Required | Description | +| ---- | :--: | :------: | :----------: | + + + + diff --git a/docs/docs.json b/docs/docs.json index e5da3472f4..18b2c1bb19 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -625,6 +625,33 @@ "examples": [ "\ncamera.zoomTo(16);\ncamera.zoomTo(16, 100);\n\n" ] + }, + { + "name": "easeTo", + "docblock": "Ease the map camera to a given camera options and animation options\n\n@param {number} x screen coordinate\n@param {number} y screen coordinate\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor to apply on current camera zoom", + "modifiers": [], + "params": [], + "returns": null, + "description": "Ease the map camera to a given camera options and animation options", + "examples": [] + }, + { + "name": "moveBy", + "docblock": "Move the map by a given screen coordinate with optional animation.\n\n@param {number} x screen coordinate\n@param {number} y screen coordinate\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", + "modifiers": [], + "params": [], + "returns": null, + "description": "Move the map by a given screen coordinate with optional animation.", + "examples": [] + }, + { + "name": "scaleBy", + "docblock": "Scale the map by with optional animation.\n@param {number} x screen coordinate\n@param {number} y screen coordinate\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", + "modifiers": [], + "params": [], + "returns": null, + "description": "Scale the map by with optional animation.", + "examples": [] } ], "props": [ diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index cc6c7be921..ca4a76e7fc 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -100,6 +100,37 @@ export interface CameraRef { flyTo: (centerCoordinate: Position, animationDuration?: number) => void; moveTo: (centerCoordinate: Position, animationDuration?: number) => void; zoomTo: (zoomLevel: number, animationDuration?: number) => void; + easeTo: (props: { + x: number; + y: number; + animationDuration?: number; + scaleFactor?: number; + }) => void; + moveBy: ( + props: + | { x: number; y: number } + | { + x: number; + y: number; + animationMode: 'easeTo' | 'linearTo'; + animationDuration: number; + }, + ) => void; + scaleBy: ( + props: + | { + x: number; + y: number; + scaleFactor: number; + } + | { + x: number; + y: number; + scaleFactor: number; + animationMode: 'easeTo' | 'linearTo'; + animationDuration: number; + }, + ) => void; } export type CameraStop = { @@ -520,6 +551,49 @@ export const Camera = memo( }; const zoomTo = useCallback(_zoomTo, [setCamera]); + const easeTo: CameraRef['easeTo'] = useCallback( + ( + easeProps, + ) => { + commands.call('easeTo', [ + easeProps.x, + easeProps.y, + easeProps.animationDuration, + easeProps.scaleFactor, + ]); + }, + [commands], + ); + + const moveBy: CameraRef['moveBy'] = useCallback( + ( + moveProps, + ) => { + commands.call('moveBy', [ + moveProps.x, + moveProps.y, + 'animationMode' in moveProps ? nativeAnimationMode(moveProps.animationMode) : null, + 'animationDuration' in moveProps ? moveProps.animationDuration : null, + ]); + }, + [commands], + ); + + const scaleBy: CameraRef['scaleBy'] = useCallback( + ( + scaleProps + ) => { + commands.call('scaleBy', [ + scaleProps.x, + scaleProps.y, + 'animationMode' in scaleProps ? nativeAnimationMode(scaleProps.animationMode) : null, + 'animationDuration' in scaleProps ? scaleProps.animationDuration : null, + scaleProps.scaleFactor, + ]); + }, + [commands], + ); + useImperativeHandle(ref, () => ({ /** * Sets any camera properties, with default fallbacks if unspecified. @@ -580,6 +654,33 @@ export const Camera = memo( * @param {number} animationDuration The transition duration */ zoomTo, + /** + * Ease the map camera to a given camera options and animation options + * + * @param {number} x screen coordinate + * @param {number} y screen coordinate + * @param {number} animationDuration The transition duration + * @param {number} scaleFactor scale factor to apply on current camera zoom + */ + easeTo, + /** + * Move the map by a given screen coordinate with optional animation. + * + * @param {number} x screen coordinate + * @param {number} y screen coordinate + * @param {NativeAnimationMode} animationMode mode used for the animation + * @param {number} animationDuration The transition duration + * @param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out + */ + moveBy, + /** + * Scale the map by with optional animation. + * @param {number} x screen coordinate + * @param {number} y screen coordinate + * @param {NativeAnimationMode} animationMode mode used for the animation + * @param {number} animationDuration The transition duration + */ + scaleBy, })); return ( diff --git a/src/specs/NativeRNMBXCameraModule.ts b/src/specs/NativeRNMBXCameraModule.ts index c153c9cba0..98282a3142 100644 --- a/src/specs/NativeRNMBXCameraModule.ts +++ b/src/specs/NativeRNMBXCameraModule.ts @@ -31,6 +31,28 @@ type ObjectOr = Object; export interface Spec extends TurboModule { updateCameraStop(viewRef: ViewRef, stop: ObjectOr): Promise; + easeTo: ( + viewRef: ViewRef, + x: number, + y: number, + animationDuration?: number, + scaleFactor?: number, + ) => Promise; + moveBy: ( + viewRef: ViewRef, + x: number, + y: number, + animationMode?: number, + animationDuration?: number, + ) => Promise; + scaleBy: ( + viewRef: ViewRef, + x: number, + y: number, + animationMode?: number, + animationDuration?: number, + scaleFactor?: number, + ) => Promise; } export default TurboModuleRegistry.getEnforcing('RNMBXCameraModule'); diff --git a/src/web/components/Camera.tsx b/src/web/components/Camera.tsx index 4b146db2bd..e4011b7d30 100644 --- a/src/web/components/Camera.tsx +++ b/src/web/components/Camera.tsx @@ -52,7 +52,7 @@ class Camera 'centerCoordinate' | 'zoomLevel' | 'minZoomLevel' | 'maxZoomLevel' > > - implements Omit + implements Omit { context!: ContextType; From 092b9690a3ed0f96c60e60af5042768cec22c795 Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Fri, 9 May 2025 13:00:22 +0200 Subject: [PATCH 02/10] cleanup --- .../com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt index 2d484cd0a0..41669a3079 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt @@ -8,7 +8,6 @@ import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.WritableNativeMap import com.mapbox.maps.ScreenCoordinate -import com.mapbox.maps.extension.style.expressions.dsl.generated.zoom import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.easeTo import com.mapbox.maps.plugin.animation.moveBy From 24aad1b91e3bf8ec5ebd2623edcc2f38b6ebd205 Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Tue, 13 May 2025 14:02:28 +0200 Subject: [PATCH 03/10] [ios] add easeTo, moveBy and scaleBy using screen coordinates --- docs/Camera.md | 6 ++-- docs/docs.json | 12 +++---- ios/RNMBX/RNMBXCamera.swift | 60 ++++++++++++++++++++++++++++++++++ ios/RNMBX/RNMBXCameraModule.mm | 39 +++++++++++++++++++++- src/components/Camera.tsx | 23 ++++++++----- 5 files changed, 121 insertions(+), 19 deletions(-) diff --git a/docs/Camera.md b/docs/Camera.md index bbad827867..0eb2b169d4 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -349,7 +349,7 @@ camera.zoomTo(16, 100); [Fit](../examples/Camera/Fit)### easeTo() -Ease the map camera to a given camera options and animation options +Ease the map camera to given absolute screen coordinates and animation options.
Can be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied. #### arguments | Name | Type | Required | Description | @@ -359,7 +359,7 @@ Ease the map camera to a given camera options and animation options ### moveBy() -Move the map by a given screen coordinate with optional animation. +Move the map by a given screen coordinate offset with optional animation.
Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied. #### arguments | Name | Type | Required | Description | @@ -369,7 +369,7 @@ Move the map by a given screen coordinate with optional animation. ### scaleBy() -Scale the map by with optional animation. +Scale the map with optional animation.
Can be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. #### arguments | Name | Type | Required | Description | diff --git a/docs/docs.json b/docs/docs.json index 18b2c1bb19..e84b2315f4 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -628,29 +628,29 @@ }, { "name": "easeTo", - "docblock": "Ease the map camera to a given camera options and animation options\n\n@param {number} x screen coordinate\n@param {number} y screen coordinate\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor to apply on current camera zoom", + "docblock": "Ease the map camera to given absolute screen coordinates and animation options.\nCan be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied.\n\n@param {number} x absolute screen coordinate\n@param {number} y absolute screen coordinate\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor to apply on current camera zoom", "modifiers": [], "params": [], "returns": null, - "description": "Ease the map camera to a given camera options and animation options", + "description": "Ease the map camera to given absolute screen coordinates and animation options.\nCan be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied.", "examples": [] }, { "name": "moveBy", - "docblock": "Move the map by a given screen coordinate with optional animation.\n\n@param {number} x screen coordinate\n@param {number} y screen coordinate\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", + "docblock": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied.\n\n@param {number} x screen coordinate offset\n@param {number} y screen coordinate offset\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", "modifiers": [], "params": [], "returns": null, - "description": "Move the map by a given screen coordinate with optional animation.", + "description": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied.", "examples": [] }, { "name": "scaleBy", - "docblock": "Scale the map by with optional animation.\n@param {number} x screen coordinate\n@param {number} y screen coordinate\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", + "docblock": "Scale the map with optional animation.\nCan be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.\n\n@param {number} x center screen coordinate\n@param {number} y center screen coordinate\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", "modifiers": [], "params": [], "returns": null, - "description": "Scale the map by with optional animation.", + "description": "Scale the map with optional animation.\nCan be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.", "examples": [] } ], diff --git a/ios/RNMBX/RNMBXCamera.swift b/ios/RNMBX/RNMBXCamera.swift index bb37cfbcfc..ca1e8d1ad1 100644 --- a/ios/RNMBX/RNMBXCamera.swift +++ b/ios/RNMBX/RNMBXCamera.swift @@ -528,6 +528,66 @@ open class RNMBXCamera : RNMBXMapComponentBase { map.mapView.viewport.removeStatusObserver(self) return super.removeFromMap(map, reason:reason) } + + @objc public func easeTo(x: Double, y: Double, animationDuration: NSNumber?, scaleFactor: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + withMapView { mapView in + let targetCoordinate = mapView.mapboxMap.coordinate(for: CGPoint(x: x, y: y)) + var cameraOptions = CameraOptions(center: targetCoordinate) + if let scaleFactor = scaleFactor { + cameraOptions.zoom = CGFloat(scaleFactor.doubleValue) + } + let duration = animationDuration?.doubleValue ?? 0.0 + + mapView.camera.ease(to: cameraOptions, duration: duration, curve: .easeInOut) { _ in + resolve(nil) + } + } + } + + @objc public func moveBy(x: Double, y: Double, animationMode: NSNumber?, animationDuration: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + withMapView { mapView in + let currentCamera = mapView.cameraState + let centerPoint = mapView.mapboxMap.point(for: currentCamera.center) + + let newPoint = CGPoint(x: centerPoint.x + x, y: centerPoint.y + y) + let newCenter = mapView.mapboxMap.coordinate(for: newPoint) + + let cameraOptions = CameraOptions(center: newCenter) + let duration = animationDuration?.doubleValue ?? 0.0 + let modeInt = animationMode?.intValue ?? 0 + let animation = CameraMode.allCases[modeInt] ?? .ease + + switch animation { + case .ease: + mapView.camera.ease(to: cameraOptions, duration: duration, curve: .easeInOut, completion: { _ in resolve(nil) }) + case .linear: + mapView.camera.ease(to: cameraOptions, duration: duration, curve: .linear, completion: { _ in resolve(nil) }) + default: + reject("E_UNSUPPORTED_ANIMATION_MODE", "unsupported animation mode", nil) + } + } + } + + @objc public func scaleBy( + x: Double, + y: Double, + scaleFactor: NSNumber, + animationDuration: NSNumber?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + withMapView { mapView in + let currentZoom = mapView.cameraState.zoom + let newZoom = currentZoom + log2(scaleFactor.doubleValue) + let anchor = CGPoint(x: x, y: y) + let cameraOptions = CameraOptions(anchor: anchor, zoom: newZoom) + let duration = animationDuration?.doubleValue ?? 0.0 + + mapView.camera.ease(to: cameraOptions, duration: duration, curve: .linear) { _ in + resolve(nil) + } + } + } } // MARK: - ViewportStatusObserver diff --git a/ios/RNMBX/RNMBXCameraModule.mm b/ios/RNMBX/RNMBXCameraModule.mm index 101ec8b140..bf814ead4d 100644 --- a/ios/RNMBX/RNMBXCameraModule.mm +++ b/ios/RNMBX/RNMBXCameraModule.mm @@ -62,7 +62,44 @@ - (void)withCamera:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXCamera *))blo [self withCamera:viewRef block:^(RNMBXCamera *view) { [view updateCameraStop: stop]; resolve(@true); - } reject:reject methodName:@"someMethod"]; + } reject:reject methodName:@"updateCameraStop"]; } +RCT_EXPORT_METHOD(easeTo:(nonnull NSNumber *)viewRef + x:(double)x + y:(double)y + animationDuration:(NSNumber *)animationDuration + scaleFactor:(NSNumber *)scaleFactor + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [self withCamera:viewRef block:^(RNMBXCamera *camera) { + [camera easeToX:x y:y animationDuration:animationDuration scaleFactor:scaleFactor resolve:resolve reject:reject]; + } reject:reject methodName:@"easeTo"]; +} + +RCT_EXPORT_METHOD(moveBy:(NSNumber *)viewRef + x:(double)x + y:(double)y + animationMode:(NSNumber *)animationMode + animationDuration:(NSNumber *)animationDuration + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [self withCamera:viewRef block:^(RNMBXCamera *camera) { + [camera moveByX:x y:y animationMode:animationMode animationDuration:animationDuration resolve:resolve reject:reject]; + } reject:reject methodName:@"moveBy"]; +} + + RCT_EXPORT_METHOD(scaleBy:(NSNumber *)viewRef + x:(double)x + y:(double)y + animationMode:(NSNumber *)animationMode + animationDuration:(NSNumber *)animationDuration + scaleFactor:(NSNumber *)scaleFactor + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [self withCamera:viewRef block:^(RNMBXCamera *camera) { + [camera scaleByX:x y:y scaleFactor:scaleFactor animationDuration:animationDuration resolve:resolve reject:reject]; + } reject:reject methodName:@"scaleBy"]; + } + @end diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index ca4a76e7fc..4838ca3e72 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -655,28 +655,33 @@ export const Camera = memo( */ zoomTo, /** - * Ease the map camera to a given camera options and animation options + * Ease the map camera to given absolute screen coordinates and animation options. + * Can be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied. * - * @param {number} x screen coordinate - * @param {number} y screen coordinate + * @param {number} x absolute screen coordinate + * @param {number} y absolute screen coordinate * @param {number} animationDuration The transition duration * @param {number} scaleFactor scale factor to apply on current camera zoom */ easeTo, /** - * Move the map by a given screen coordinate with optional animation. + * Move the map by a given screen coordinate offset with optional animation. + * Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied. * - * @param {number} x screen coordinate - * @param {number} y screen coordinate + * @param {number} x screen coordinate offset + * @param {number} y screen coordinate offset * @param {NativeAnimationMode} animationMode mode used for the animation * @param {number} animationDuration The transition duration * @param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out */ moveBy, /** - * Scale the map by with optional animation. - * @param {number} x screen coordinate - * @param {number} y screen coordinate + * Scale the map with optional animation. + * Can be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. + * + * @param {number} x center screen coordinate + * @param {number} y center screen coordinate + * @param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out * @param {NativeAnimationMode} animationMode mode used for the animation * @param {number} animationDuration The transition duration */ From 84614f3375c6166ed5a78045d16683cb75259195 Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Tue, 13 May 2025 14:09:46 +0200 Subject: [PATCH 04/10] update docs --- docs/Camera.md | 4 ++-- docs/docs.json | 8 ++++---- src/components/Camera.tsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/Camera.md b/docs/Camera.md index 0eb2b169d4..7a7f6c3598 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -359,7 +359,7 @@ Ease the map camera to given absolute screen coordinates and animation options.< ### moveBy() -Move the map by a given screen coordinate offset with optional animation.
Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied. +Move the map by a given screen coordinate offset with optional animation.
Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied. #### arguments | Name | Type | Required | Description | @@ -369,7 +369,7 @@ Move the map by a given screen coordinate offset with optional animation.
Ca ### scaleBy() -Scale the map with optional animation.
Can be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. +Scale the map with optional animation.
Can be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. #### arguments | Name | Type | Required | Description | diff --git a/docs/docs.json b/docs/docs.json index e84b2315f4..f46774b031 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -637,20 +637,20 @@ }, { "name": "moveBy", - "docblock": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied.\n\n@param {number} x screen coordinate offset\n@param {number} y screen coordinate offset\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", + "docblock": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied.\n\n@param {number} x screen coordinate offset\n@param {number} y screen coordinate offset\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", "modifiers": [], "params": [], "returns": null, - "description": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied.", + "description": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied.", "examples": [] }, { "name": "scaleBy", - "docblock": "Scale the map with optional animation.\nCan be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.\n\n@param {number} x center screen coordinate\n@param {number} y center screen coordinate\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", + "docblock": "Scale the map with optional animation.\nCan be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.\n\n@param {number} x center screen coordinate\n@param {number} y center screen coordinate\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", "modifiers": [], "params": [], "returns": null, - "description": "Scale the map with optional animation.\nCan be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.", + "description": "Scale the map with optional animation.\nCan be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.", "examples": [] } ], diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index 4838ca3e72..cfa5931b53 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -666,7 +666,7 @@ export const Camera = memo( easeTo, /** * Move the map by a given screen coordinate offset with optional animation. - * Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) drag gesture applied. + * Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied. * * @param {number} x screen coordinate offset * @param {number} y screen coordinate offset @@ -677,7 +677,7 @@ export const Camera = memo( moveBy, /** * Scale the map with optional animation. - * Can be used to get Android Auto pan gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. + * Can be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. * * @param {number} x center screen coordinate * @param {number} y center screen coordinate From b69c6f4f7185a5970727ad10eddfb245a04c1ad1 Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Tue, 13 May 2025 15:37:46 +0200 Subject: [PATCH 05/10] align ios camera animation mode to androids numeric values --- ios/RNMBX/RNMBXCamera.swift | 32 ++++++++++++++++------------ ios/RNMBX/RNMBXCameraModule.mm | 2 +- src/components/Camera.tsx | 8 ++++++- src/specs/NativeRNMBXCameraModule.ts | 4 +--- src/specs/codegenUtils.ts | 2 +- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/ios/RNMBX/RNMBXCamera.swift b/ios/RNMBX/RNMBXCamera.swift index ca1e8d1ad1..5b33661393 100644 --- a/ios/RNMBX/RNMBXCamera.swift +++ b/ios/RNMBX/RNMBXCamera.swift @@ -22,8 +22,12 @@ public protocol RNMBXMapComponent: AnyObject { func waitForStyleLoad() -> Bool } -enum CameraMode: String, CaseIterable { - case flight, ease, linear, none +enum CameraMode: Int { + case flight = 1 + case ease = 2 + case linear = 3 + case move = 4 + case none = 5 } enum UserTrackingMode: String { @@ -477,7 +481,7 @@ open class RNMBXCamera : RNMBXMapComponentBase { } var mode: CameraMode = .flight - if let m = stop["mode"] as? String, let m = CameraMode(rawValue: m) { + if let m = stop["mode"] as? NSNumber, let m = CameraMode(rawValue: m.intValue) { mode = m } @@ -554,17 +558,12 @@ open class RNMBXCamera : RNMBXMapComponentBase { let cameraOptions = CameraOptions(center: newCenter) let duration = animationDuration?.doubleValue ?? 0.0 - let modeInt = animationMode?.intValue ?? 0 - let animation = CameraMode.allCases[modeInt] ?? .ease - - switch animation { - case .ease: - mapView.camera.ease(to: cameraOptions, duration: duration, curve: .easeInOut, completion: { _ in resolve(nil) }) - case .linear: - mapView.camera.ease(to: cameraOptions, duration: duration, curve: .linear, completion: { _ in resolve(nil) }) - default: - reject("E_UNSUPPORTED_ANIMATION_MODE", "unsupported animation mode", nil) + var curve: UIView.AnimationCurve = .linear + if let m = animationMode?.intValue, let m = CameraMode(rawValue: m) { + curve = m == CameraMode.ease ? .easeInOut : .linear } + + mapView.camera.ease(to: cameraOptions, duration: duration, curve: curve, completion: { _ in resolve(nil) }) } } @@ -572,6 +571,7 @@ open class RNMBXCamera : RNMBXMapComponentBase { x: Double, y: Double, scaleFactor: NSNumber, + animationMode: NSNumber?, animationDuration: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock @@ -582,8 +582,12 @@ open class RNMBXCamera : RNMBXMapComponentBase { let anchor = CGPoint(x: x, y: y) let cameraOptions = CameraOptions(anchor: anchor, zoom: newZoom) let duration = animationDuration?.doubleValue ?? 0.0 + var curve: UIView.AnimationCurve = .linear + if let m = animationMode?.intValue, let m = CameraMode(rawValue: m) { + curve = m == CameraMode.ease ? .easeInOut : .linear + } - mapView.camera.ease(to: cameraOptions, duration: duration, curve: .linear) { _ in + mapView.camera.ease(to: cameraOptions, duration: duration, curve: curve) { _ in resolve(nil) } } diff --git a/ios/RNMBX/RNMBXCameraModule.mm b/ios/RNMBX/RNMBXCameraModule.mm index bf814ead4d..c3746b675a 100644 --- a/ios/RNMBX/RNMBXCameraModule.mm +++ b/ios/RNMBX/RNMBXCameraModule.mm @@ -98,7 +98,7 @@ - (void)withCamera:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXCamera *))blo resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { [self withCamera:viewRef block:^(RNMBXCamera *camera) { - [camera scaleByX:x y:y scaleFactor:scaleFactor animationDuration:animationDuration resolve:resolve reject:reject]; + [camera scaleByX:x y:y scaleFactor:scaleFactor animationMode:animationMode animationDuration:animationDuration resolve:resolve reject:reject]; } reject:reject methodName:@"scaleBy"]; } diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index cfa5931b53..182c7b174f 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -61,7 +61,13 @@ const nativeAnimationMode = ( // Native module types. -type NativeAnimationMode = 'flight' | 'ease' | 'linear' | 'none' | 'move'; +type FLIGHT = 1; +type EASE = 2; +type LINEAR = 3; +type MOVE = 4; +type NONE = 5; + +type NativeAnimationMode = FLIGHT | EASE | LINEAR | MOVE | NONE; interface NativeCameraProps extends CameraFollowConfig { testID?: string; diff --git a/src/specs/NativeRNMBXCameraModule.ts b/src/specs/NativeRNMBXCameraModule.ts index 98282a3142..8aea975b71 100644 --- a/src/specs/NativeRNMBXCameraModule.ts +++ b/src/specs/NativeRNMBXCameraModule.ts @@ -15,7 +15,7 @@ interface NativeCameraStop { paddingTop?: number; paddingBottom?: number; duration?: number; - mode?: NativeAnimationMode; + mode?: number; } type Stop = @@ -24,8 +24,6 @@ type Stop = } | NativeCameraStop; -type NativeAnimationMode = 'flight' | 'ease' | 'linear' | 'none' | 'move'; - // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-unused-vars type ObjectOr = Object; diff --git a/src/specs/codegenUtils.ts b/src/specs/codegenUtils.ts index a15d962c37..c55e702e8e 100644 --- a/src/specs/codegenUtils.ts +++ b/src/specs/codegenUtils.ts @@ -25,5 +25,5 @@ export type NativeCameraStop = { paddingTop?: Double; paddingBottom?: Double; duration?: Double; - mode?: string; + mode?: number; } | null; From 31e4b4d11abfaa23e72705fb207c39bf0aa66eb0 Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Tue, 13 May 2025 16:15:45 +0200 Subject: [PATCH 06/10] fix: nonnull error on ios --- .../components/camera/RNMBXCameraModule.kt | 22 +++++++++---------- .../rnmbx/NativeRNMBXCameraModuleSpec.java | 6 ++--- src/components/Camera.tsx | 12 +++++----- src/specs/NativeRNMBXCameraModule.ts | 14 ++++++------ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt index 41669a3079..bd2d86b05f 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt @@ -58,16 +58,16 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V } private fun getAnimationOptions( - animationMode: Double?, - animationDuration: Double? + animationMode: Double, + animationDuration: Double ): MapAnimationOptions { return MapAnimationOptions.Builder() .apply { - when (animationMode?.toInt()) { + when (animationMode.toInt()) { CameraMode.LINEAR -> interpolator(LinearInterpolator()) CameraMode.EASE -> interpolator(AccelerateDecelerateInterpolator()) } - animationDuration?.let { duration -> + animationDuration.let { duration -> duration(duration.toLong()) } } @@ -78,8 +78,8 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V viewRef: ViewRefTag?, x: Double, y: Double, - animationMode: Double?, - animationDuration: Double?, + animationMode: Double, + animationDuration: Double, promise: Promise ) { withViewportOnUIThread(viewRef, promise) { @@ -96,8 +96,8 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V viewRef: ViewRefTag?, x: Double, y: Double, - animationDuration: Double?, - scaleFactor: Double?, + animationDuration: Double, + scaleFactor: Double, promise: Promise ) { withViewportOnUIThread(viewRef, promise) { @@ -124,9 +124,9 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V viewRef: ViewRefTag?, x: Double, y: Double, - animationMode: Double?, - animationDuration: Double?, - scaleFactor: Double?, + animationMode: Double, + animationDuration: Double, + scaleFactor: Double, promise: Promise ) { withViewportOnUIThread(viewRef, promise) { diff --git a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java index fd9ad09af1..b617cce91a 100644 --- a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java +++ b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java @@ -41,13 +41,13 @@ public NativeRNMBXCameraModuleSpec(ReactApplicationContext reactContext) { @ReactMethod @DoNotStrip - public abstract void easeTo(@Nullable Double viewRef, double x, double y, @Nullable Double animationDuration, @Nullable Double scaleFactor, Promise promise); + public abstract void easeTo(@Nullable Double viewRef, double x, double y, double animationDuration, double scaleFactor, Promise promise); @ReactMethod @DoNotStrip - public abstract void moveBy(@Nullable Double viewRef, double x, double y, @Nullable Double animationMode, @Nullable Double animationDuration, Promise promise); + public abstract void moveBy(@Nullable Double viewRef, double x, double y, double animationMode, double animationDuration, Promise promise); @ReactMethod @DoNotStrip - public abstract void scaleBy(@Nullable Double viewRef, double x, double y, @Nullable Double animationMode, @Nullable Double animationDuration, @Nullable Double scaleFactor, Promise promise); + public abstract void scaleBy(@Nullable Double viewRef, double x, double y, double animationMode, double animationDuration, double scaleFactor, Promise promise); } diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index 182c7b174f..d8dea29d9b 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -564,8 +564,8 @@ export const Camera = memo( commands.call('easeTo', [ easeProps.x, easeProps.y, - easeProps.animationDuration, - easeProps.scaleFactor, + easeProps.animationDuration ?? 0, + easeProps.scaleFactor ?? 1.0, ]); }, [commands], @@ -578,8 +578,8 @@ export const Camera = memo( commands.call('moveBy', [ moveProps.x, moveProps.y, - 'animationMode' in moveProps ? nativeAnimationMode(moveProps.animationMode) : null, - 'animationDuration' in moveProps ? moveProps.animationDuration : null, + 'animationMode' in moveProps ? nativeAnimationMode(moveProps.animationMode) : nativeAnimationMode('linearTo'), + 'animationDuration' in moveProps ? moveProps.animationDuration : 0, ]); }, [commands], @@ -592,8 +592,8 @@ export const Camera = memo( commands.call('scaleBy', [ scaleProps.x, scaleProps.y, - 'animationMode' in scaleProps ? nativeAnimationMode(scaleProps.animationMode) : null, - 'animationDuration' in scaleProps ? scaleProps.animationDuration : null, + 'animationMode' in scaleProps ? nativeAnimationMode(scaleProps.animationMode) : nativeAnimationMode('linearTo'), + 'animationDuration' in scaleProps ? scaleProps.animationDuration : 0, scaleProps.scaleFactor, ]); }, diff --git a/src/specs/NativeRNMBXCameraModule.ts b/src/specs/NativeRNMBXCameraModule.ts index 8aea975b71..7b3e1a8e17 100644 --- a/src/specs/NativeRNMBXCameraModule.ts +++ b/src/specs/NativeRNMBXCameraModule.ts @@ -33,23 +33,23 @@ export interface Spec extends TurboModule { viewRef: ViewRef, x: number, y: number, - animationDuration?: number, - scaleFactor?: number, + animationDuration: number, + scaleFactor: number, ) => Promise; moveBy: ( viewRef: ViewRef, x: number, y: number, - animationMode?: number, - animationDuration?: number, + animationMode: number, + animationDuration: number, ) => Promise; scaleBy: ( viewRef: ViewRef, x: number, y: number, - animationMode?: number, - animationDuration?: number, - scaleFactor?: number, + animationMode: number, + animationDuration: number, + scaleFactor: number, ) => Promise; } From 7bd66d7efdd02a59e37a75e73e9b8af0ca6f16ae Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Wed, 14 May 2025 12:48:55 +0200 Subject: [PATCH 07/10] final adjustments --- ios/RNMBX/RNMBXCamera.swift | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ios/RNMBX/RNMBXCamera.swift b/ios/RNMBX/RNMBXCamera.swift index 5b33661393..7109115ecb 100644 --- a/ios/RNMBX/RNMBXCamera.swift +++ b/ios/RNMBX/RNMBXCamera.swift @@ -540,29 +540,34 @@ open class RNMBXCamera : RNMBXMapComponentBase { if let scaleFactor = scaleFactor { cameraOptions.zoom = CGFloat(scaleFactor.doubleValue) } - let duration = animationDuration?.doubleValue ?? 0.0 + let duration = (animationDuration?.doubleValue ?? 500) / 1000 mapView.camera.ease(to: cameraOptions, duration: duration, curve: .easeInOut) { _ in resolve(nil) } } } - + @objc public func moveBy(x: Double, y: Double, animationMode: NSNumber?, animationDuration: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { withMapView { mapView in - let currentCamera = mapView.cameraState - let centerPoint = mapView.mapboxMap.point(for: currentCamera.center) + let contentFrame = mapView.bounds.inset(by: mapView.safeAreaInsets) + let centerPoint = CGPoint(x: contentFrame.midX, y: contentFrame.midY) + let endCameraPoint = CGPoint(x: centerPoint.x + x, y: centerPoint.y + y) + let cameraOptions = mapView.mapboxMap.dragCameraOptions(from: centerPoint, to: endCameraPoint) + + let duration = (animationDuration?.doubleValue ?? 0.0) / 1000 - let newPoint = CGPoint(x: centerPoint.x + x, y: centerPoint.y + y) - let newCenter = mapView.mapboxMap.coordinate(for: newPoint) + if (duration == 0.0) { + mapView.mapboxMap.setCamera(to: cameraOptions) + resolve(nil) + return + } - let cameraOptions = CameraOptions(center: newCenter) - let duration = animationDuration?.doubleValue ?? 0.0 var curve: UIView.AnimationCurve = .linear if let m = animationMode?.intValue, let m = CameraMode(rawValue: m) { curve = m == CameraMode.ease ? .easeInOut : .linear } - + mapView.camera.ease(to: cameraOptions, duration: duration, curve: curve, completion: { _ in resolve(nil) }) } } @@ -581,7 +586,14 @@ open class RNMBXCamera : RNMBXMapComponentBase { let newZoom = currentZoom + log2(scaleFactor.doubleValue) let anchor = CGPoint(x: x, y: y) let cameraOptions = CameraOptions(anchor: anchor, zoom: newZoom) - let duration = animationDuration?.doubleValue ?? 0.0 + let duration = (animationDuration?.doubleValue ?? 0.0) / 1000 + + if (duration == 0.0) { + mapView.mapboxMap.setCamera(to: cameraOptions) + resolve(nil) + return + } + var curve: UIView.AnimationCurve = .linear if let m = animationMode?.intValue, let m = CameraMode(rawValue: m) { curve = m == CameraMode.ease ? .easeInOut : .linear From 4e8ee038c6fb91405300afd9e279432bd8d7b44b Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Wed, 14 May 2025 14:50:54 +0200 Subject: [PATCH 08/10] fix missing nonnull --- ios/RNMBX/RNMBXCameraModule.mm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/RNMBX/RNMBXCameraModule.mm b/ios/RNMBX/RNMBXCameraModule.mm index c3746b675a..171281e97f 100644 --- a/ios/RNMBX/RNMBXCameraModule.mm +++ b/ios/RNMBX/RNMBXCameraModule.mm @@ -68,8 +68,8 @@ - (void)withCamera:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXCamera *))blo RCT_EXPORT_METHOD(easeTo:(nonnull NSNumber *)viewRef x:(double)x y:(double)y - animationDuration:(NSNumber *)animationDuration - scaleFactor:(NSNumber *)scaleFactor + animationDuration:(nonnull NSNumber *)animationDuration + scaleFactor:(nonnull NSNumber *)scaleFactor resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { [self withCamera:viewRef block:^(RNMBXCamera *camera) { @@ -77,11 +77,11 @@ - (void)withCamera:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXCamera *))blo } reject:reject methodName:@"easeTo"]; } -RCT_EXPORT_METHOD(moveBy:(NSNumber *)viewRef +RCT_EXPORT_METHOD(moveBy:(nonnull NSNumber *)viewRef x:(double)x y:(double)y - animationMode:(NSNumber *)animationMode - animationDuration:(NSNumber *)animationDuration + animationMode:(nonnull NSNumber *)animationMode + animationDuration:(nonnull NSNumber *)animationDuration resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { [self withCamera:viewRef block:^(RNMBXCamera *camera) { @@ -89,12 +89,12 @@ - (void)withCamera:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXCamera *))blo } reject:reject methodName:@"moveBy"]; } - RCT_EXPORT_METHOD(scaleBy:(NSNumber *)viewRef + RCT_EXPORT_METHOD(scaleBy:(nonnull NSNumber *)viewRef x:(double)x y:(double)y - animationMode:(NSNumber *)animationMode - animationDuration:(NSNumber *)animationDuration - scaleFactor:(NSNumber *)scaleFactor + animationMode:(nonnull NSNumber *)animationMode + animationDuration:(nonnull NSNumber *)animationDuration + scaleFactor:(nonnull NSNumber *)scaleFactor resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { [self withCamera:viewRef block:^(RNMBXCamera *camera) { From e4f078d3e1ad0401bb90c37c497b777ec9b55a8d Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Thu, 15 May 2025 13:29:40 +0200 Subject: [PATCH 09/10] rm easeTo --- .../components/camera/RNMBXCameraModule.kt | 28 ----------------- .../rnmbx/NativeRNMBXCameraModuleSpec.java | 4 --- docs/Camera.md | 12 +------- docs/docs.json | 9 ------ ios/RNMBX/RNMBXCamera.swift | 17 +---------- ios/RNMBX/RNMBXCameraModule.mm | 12 -------- src/components/Camera.tsx | 30 ------------------- src/specs/NativeRNMBXCameraModule.ts | 7 ----- src/web/components/Camera.tsx | 2 +- 9 files changed, 3 insertions(+), 118 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt index bd2d86b05f..dce3f7c34a 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt @@ -92,34 +92,6 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V } } - override fun easeTo( - viewRef: ViewRefTag?, - x: Double, - y: Double, - animationDuration: Double, - scaleFactor: Double, - promise: Promise - ) { - withViewportOnUIThread(viewRef, promise) { - it.mapboxMap?.let { map -> - val cameraOptions = - map.cameraState.toCameraOptions().toBuilder().apply { - scaleFactor?.let { scale -> - zoom(map.cameraState.zoom + scale) - } - center(map.coordinateForPixel(ScreenCoordinate(x, y))) - }.build() - - val animationOptions = - getAnimationOptions(CameraMode.EASE.toDouble(), animationDuration) - - map.easeTo(cameraOptions, animationOptions) - - promise.resolve(null) - } - } - } - override fun scaleBy( viewRef: ViewRefTag?, x: Double, diff --git a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java index b617cce91a..35444a4a89 100644 --- a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java +++ b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeRNMBXCameraModuleSpec.java @@ -39,10 +39,6 @@ public NativeRNMBXCameraModuleSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void updateCameraStop(@Nullable Double viewRef, ReadableMap stop, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void easeTo(@Nullable Double viewRef, double x, double y, double animationDuration, double scaleFactor, Promise promise); - @ReactMethod @DoNotStrip public abstract void moveBy(@Nullable Double viewRef, double x, double y, double animationMode, double animationDuration, Promise promise); diff --git a/docs/Camera.md b/docs/Camera.md index 7a7f6c3598..b7c62b5fe7 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -347,17 +347,7 @@ camera.zoomTo(16, 100); ``` -[Fit](../examples/Camera/Fit)### easeTo() - -Ease the map camera to given absolute screen coordinates and animation options.
Can be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied. - -#### arguments -| Name | Type | Required | Description | -| ---- | :--: | :------: | :----------: | - - - -### moveBy() +[Fit](../examples/Camera/Fit)### moveBy() Move the map by a given screen coordinate offset with optional animation.
Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied. diff --git a/docs/docs.json b/docs/docs.json index f46774b031..de15e6b8df 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -626,15 +626,6 @@ "\ncamera.zoomTo(16);\ncamera.zoomTo(16, 100);\n\n" ] }, - { - "name": "easeTo", - "docblock": "Ease the map camera to given absolute screen coordinates and animation options.\nCan be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied.\n\n@param {number} x absolute screen coordinate\n@param {number} y absolute screen coordinate\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor to apply on current camera zoom", - "modifiers": [], - "params": [], - "returns": null, - "description": "Ease the map camera to given absolute screen coordinates and animation options.\nCan be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied.", - "examples": [] - }, { "name": "moveBy", "docblock": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied.\n\n@param {number} x screen coordinate offset\n@param {number} y screen coordinate offset\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", diff --git a/ios/RNMBX/RNMBXCamera.swift b/ios/RNMBX/RNMBXCamera.swift index 7109115ecb..69c7e2777a 100644 --- a/ios/RNMBX/RNMBXCamera.swift +++ b/ios/RNMBX/RNMBXCamera.swift @@ -532,22 +532,7 @@ open class RNMBXCamera : RNMBXMapComponentBase { map.mapView.viewport.removeStatusObserver(self) return super.removeFromMap(map, reason:reason) } - - @objc public func easeTo(x: Double, y: Double, animationDuration: NSNumber?, scaleFactor: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - withMapView { mapView in - let targetCoordinate = mapView.mapboxMap.coordinate(for: CGPoint(x: x, y: y)) - var cameraOptions = CameraOptions(center: targetCoordinate) - if let scaleFactor = scaleFactor { - cameraOptions.zoom = CGFloat(scaleFactor.doubleValue) - } - let duration = (animationDuration?.doubleValue ?? 500) / 1000 - - mapView.camera.ease(to: cameraOptions, duration: duration, curve: .easeInOut) { _ in - resolve(nil) - } - } - } - + @objc public func moveBy(x: Double, y: Double, animationMode: NSNumber?, animationDuration: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { withMapView { mapView in let contentFrame = mapView.bounds.inset(by: mapView.safeAreaInsets) diff --git a/ios/RNMBX/RNMBXCameraModule.mm b/ios/RNMBX/RNMBXCameraModule.mm index 171281e97f..b9a1577d9a 100644 --- a/ios/RNMBX/RNMBXCameraModule.mm +++ b/ios/RNMBX/RNMBXCameraModule.mm @@ -65,18 +65,6 @@ - (void)withCamera:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXCamera *))blo } reject:reject methodName:@"updateCameraStop"]; } -RCT_EXPORT_METHOD(easeTo:(nonnull NSNumber *)viewRef - x:(double)x - y:(double)y - animationDuration:(nonnull NSNumber *)animationDuration - scaleFactor:(nonnull NSNumber *)scaleFactor - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject) { - [self withCamera:viewRef block:^(RNMBXCamera *camera) { - [camera easeToX:x y:y animationDuration:animationDuration scaleFactor:scaleFactor resolve:resolve reject:reject]; - } reject:reject methodName:@"easeTo"]; -} - RCT_EXPORT_METHOD(moveBy:(nonnull NSNumber *)viewRef x:(double)x y:(double)y diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index d8dea29d9b..8d70fbf05d 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -106,12 +106,6 @@ export interface CameraRef { flyTo: (centerCoordinate: Position, animationDuration?: number) => void; moveTo: (centerCoordinate: Position, animationDuration?: number) => void; zoomTo: (zoomLevel: number, animationDuration?: number) => void; - easeTo: (props: { - x: number; - y: number; - animationDuration?: number; - scaleFactor?: number; - }) => void; moveBy: ( props: | { x: number; y: number } @@ -557,20 +551,6 @@ export const Camera = memo( }; const zoomTo = useCallback(_zoomTo, [setCamera]); - const easeTo: CameraRef['easeTo'] = useCallback( - ( - easeProps, - ) => { - commands.call('easeTo', [ - easeProps.x, - easeProps.y, - easeProps.animationDuration ?? 0, - easeProps.scaleFactor ?? 1.0, - ]); - }, - [commands], - ); - const moveBy: CameraRef['moveBy'] = useCallback( ( moveProps, @@ -660,16 +640,6 @@ export const Camera = memo( * @param {number} animationDuration The transition duration */ zoomTo, - /** - * Ease the map camera to given absolute screen coordinates and animation options. - * Can be used to get the Android Auto double tap gesture (onScale with scaleFactor == 2.0) applied. - * - * @param {number} x absolute screen coordinate - * @param {number} y absolute screen coordinate - * @param {number} animationDuration The transition duration - * @param {number} scaleFactor scale factor to apply on current camera zoom - */ - easeTo, /** * Move the map by a given screen coordinate offset with optional animation. * Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied. diff --git a/src/specs/NativeRNMBXCameraModule.ts b/src/specs/NativeRNMBXCameraModule.ts index 7b3e1a8e17..e87dc1a888 100644 --- a/src/specs/NativeRNMBXCameraModule.ts +++ b/src/specs/NativeRNMBXCameraModule.ts @@ -29,13 +29,6 @@ type ObjectOr = Object; export interface Spec extends TurboModule { updateCameraStop(viewRef: ViewRef, stop: ObjectOr): Promise; - easeTo: ( - viewRef: ViewRef, - x: number, - y: number, - animationDuration: number, - scaleFactor: number, - ) => Promise; moveBy: ( viewRef: ViewRef, x: number, diff --git a/src/web/components/Camera.tsx b/src/web/components/Camera.tsx index e4011b7d30..2bde971a14 100644 --- a/src/web/components/Camera.tsx +++ b/src/web/components/Camera.tsx @@ -52,7 +52,7 @@ class Camera 'centerCoordinate' | 'zoomLevel' | 'minZoomLevel' | 'maxZoomLevel' > > - implements Omit + implements Omit { context!: ContextType; From d3f50f16d2b7486d2a3e7531c4111ddae4f996ca Mon Sep 17 00:00:00 2001 From: g4rb4g3 Date: Thu, 15 May 2025 14:56:46 +0200 Subject: [PATCH 10/10] update docs --- .../rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt | 2 +- docs/Camera.md | 4 ++-- docs/docs.json | 8 ++++---- src/components/Camera.tsx | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt index dce3f7c34a..722903d706 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/camera/RNMBXCameraModule.kt @@ -106,7 +106,7 @@ class RNMBXCameraModule(context: ReactApplicationContext, val viewTagResolver: V val animationOptions = getAnimationOptions(animationMode, animationDuration) - map.scaleBy(scaleFactor ?: 1.0, ScreenCoordinate(x, y), animationOptions) + map.scaleBy(scaleFactor, ScreenCoordinate(x, y), animationOptions) promise.resolve(null) } diff --git a/docs/Camera.md b/docs/Camera.md index b7c62b5fe7..18fcac86c6 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -349,7 +349,7 @@ camera.zoomTo(16, 100); [Fit](../examples/Camera/Fit)### moveBy() -Move the map by a given screen coordinate offset with optional animation.
Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied. +Move the map by a given screen coordinate offset with optional animation.
Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied, for these to work properly do not specify animationDuration. #### arguments | Name | Type | Required | Description | @@ -359,7 +359,7 @@ Move the map by a given screen coordinate offset with optional animation.
Ca ### scaleBy() -Scale the map with optional animation.
Can be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. +Scale the map with optional animation.
Can be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) or Android Auto double tap (onScale with scaleFactor == 2.0) applied. #### arguments | Name | Type | Required | Description | diff --git a/docs/docs.json b/docs/docs.json index de15e6b8df..c9b3b040f8 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -628,20 +628,20 @@ }, { "name": "moveBy", - "docblock": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied.\n\n@param {number} x screen coordinate offset\n@param {number} y screen coordinate offset\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", + "docblock": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied, for these to work properly do not specify animationDuration.\n\n@param {number} x screen coordinate offset\n@param {number} y screen coordinate offset\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out", "modifiers": [], "params": [], "returns": null, - "description": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied.", + "description": "Move the map by a given screen coordinate offset with optional animation.\nCan be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied, for these to work properly do not specify animationDuration.", "examples": [] }, { "name": "scaleBy", - "docblock": "Scale the map with optional animation.\nCan be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.\n\n@param {number} x center screen coordinate\n@param {number} y center screen coordinate\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", + "docblock": "Scale the map with optional animation.\nCan be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) or Android Auto double tap (onScale with scaleFactor == 2.0) applied.\n\n@param {number} x center screen coordinate\n@param {number} y center screen coordinate\n@param {number} scaleFactor scale factor value > 0.0 and < 2.0 when 1.0 means no scaling, > 1.0 zoom in and < 1.0 zoom out\n@param {NativeAnimationMode} animationMode mode used for the animation\n@param {number} animationDuration The transition duration", "modifiers": [], "params": [], "returns": null, - "description": "Scale the map with optional animation.\nCan be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied.", + "description": "Scale the map with optional animation.\nCan be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) or Android Auto double tap (onScale with scaleFactor == 2.0) applied.", "examples": [] } ], diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index 8d70fbf05d..ee6dbc228b 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -642,7 +642,7 @@ export const Camera = memo( zoomTo, /** * Move the map by a given screen coordinate offset with optional animation. - * Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied. + * Can be used to get the Android Auto (onScroll) or Carplay(mapTemplate didUpdatePanGestureWithTranslation) pan gesture applied, for these to work properly do not specify animationDuration. * * @param {number} x screen coordinate offset * @param {number} y screen coordinate offset @@ -653,7 +653,7 @@ export const Camera = memo( moveBy, /** * Scale the map with optional animation. - * Can be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) applied. + * Can be used to get Android Auto pinch gesture (onScale with scaleFactor > 0.0 and < 2.0) or Android Auto double tap (onScale with scaleFactor == 2.0) applied. * * @param {number} x center screen coordinate * @param {number} y center screen coordinate