Skip to content

Commit

Permalink
feat: implement MovePointShapeAnimator and `ChangeLineOffsetsShapeA…
Browse files Browse the repository at this point in the history
…nimator` (#3281)

* Fix invalid return and property errors

* Re-add layout metric prop

* Fix invalid return and property errors

* Re-add layout metric prop

* Fix invalid return and property errors

* Finish move-point implementation and refactor common animator

* Add animator type

* Fix duration unit and rename variables for clarity

* Add new animated point with animator example

* Remove log

* Always start new animation from current position

* Fix accidental rename

* Re-add getShape and override it

* Rename arg

* Update lockfiles

* Fix prop

* Move interface to module index file

* Provide timestamp instead of delta for comparisons

* Fix import

* Implement line-offset animator

* Implement end offset animation

* Choose random offsets based on line length

* Add reset button and improve copy

* Move global duration into function arg

* Rename coordinate arg for consistency

* Undo delete

* Expose method for updating entire line

* Revert instant shape update

* Add and update reject calls on error

* Clean up

* Avoid syntax warnings

* Implement move-point animator

* Implement line-offsets animator on Android

* Add checks to avoid illegal turf measurements

* Restart animator if stopped when setting new target values

* Fix whitespace

* Provide default return value for getShape() like on Android

* Clean up passthrough function

* Return empty feature collection to avoid crash-on-render 0-length line

* Switch to sliders and allow offsets to sum greater than line length

* Refactor offset overflow handling

* Update generated files

* Increase padding

* Fix divide-by-zero and refactor

* Clean up imports

* Remove second element from getAnimatedShape return value

* Fix name

* Unsubscribe consumer on removal from map

* Conditionally start and stop timer based on animation progress

* Remove unneeded null check

* Port timer start/stop logic to point animator

* Abstract out animatable value helper class

* Rename directory

* Fix timestamp arg

* Remove unneeded start method from JS interface

* Add padding

* Clean up imports

* Port new logic to iOS implementation

* Enable randomizing line

* Abstract out single-refresh logic and make public

* Add refresh to protocol

* Implement single-refresh functionality

* Fix timer lifespan calculation

* Keep randomized line centered

* Switch from Timer to CADisplayLink

* Switch unit entirely to seconds on native side

* Set completion ratio to 1 if duration is 0

* Accept optional offsets when setting new line

* Port optional offsets when updating line to Android

* Rename timestamp for clarity

* Fix syntax bug

* Simplify literal declarations

* Correctly handle zero duration for moving point

* Prevent asymmetrical animator consumer removal

* Augment log

* Port animator subscriber removal bug to iOS

* Refactor

* Refactor and allow setting duration

* Test don't unsubscribe for any remove reason

* Re-enable unsubscribe on style change

* Add alert log for source remove reason

* Unsubscribe only on view removal

* Upgrade package to avoid new-arch error

* Ignore .cxx (generated during new-arch build)

* Remove unused start method

* Update lockfile

* Refactor logger to enable tag and deprecate quasi-duplicate functions

* Remove log

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>

* Remove Page component

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>

* Remove Page component

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>

* Remove log

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>

* Fix component removal

* Unusbscribe on new shape input in case of changed animator

* Remove logs

* Re-add v11 compat import and clean up imports

* Allow setting line offsets with zero duration

* Re-add example and fix references

* Remove ruby version lock

* Update lockfile

* Make error enum public

* Add animator stub

* Specify Swift version in action

* Make Swift version unspecified

* Revert CI action to use Swift 5.1

* Add explicit returns

* Remove empty swift version lock

* Remove swift version

* Switch to rnmbx logger

* Re-add Swift version specifier

---------

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>
  • Loading branch information
naftalibeder and mfazekas committed Feb 23, 2024
1 parent f5bdc04 commit c5d94e6
Show file tree
Hide file tree
Showing 42 changed files with 1,872 additions and 437 deletions.
15 changes: 13 additions & 2 deletions android/src/main/java/com/rnmapbox/rnmbx/RNMBXPackage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ import com.rnmapbox.rnmbx.modules.RNMBXOfflineModule
import com.rnmapbox.rnmbx.modules.RNMBXOfflineModuleLegacy
import com.rnmapbox.rnmbx.modules.RNMBXSnapshotModule
import com.rnmapbox.rnmbx.modules.RNMBXTileStoreModule
import com.rnmapbox.rnmbx.shape_animators.RNMBXMovePointShapeAnimatorModule
import com.rnmapbox.rnmbx.shape_animators.ShapeAnimatorManager
import com.rnmapbox.rnmbx.shapeAnimators.RNMBXChangeLineOffsetsShapeAnimatorModule
import com.rnmapbox.rnmbx.shapeAnimators.RNMBXMovePointShapeAnimatorModule
import com.rnmapbox.rnmbx.shapeAnimators.ShapeAnimatorManager
import com.rnmapbox.rnmbx.utils.ViewTagResolver

class RNMBXPackage : TurboReactPackage() {
Expand Down Expand Up @@ -103,6 +104,7 @@ class RNMBXPackage : TurboReactPackage() {
RNMBXImageModule.NAME -> return RNMBXImageModule(reactApplicationContext, getViewTagResolver(reactApplicationContext, s))
RNMBXPointAnnotationModule.NAME -> return RNMBXPointAnnotationModule(reactApplicationContext, getViewTagResolver(reactApplicationContext, s))
RNMBXMovePointShapeAnimatorModule.NAME -> return RNMBXMovePointShapeAnimatorModule(reactApplicationContext, getShapeAnimators(s))
RNMBXChangeLineOffsetsShapeAnimatorModule.NAME -> return RNMBXChangeLineOffsetsShapeAnimatorModule(reactApplicationContext, getShapeAnimators(s))
}
return null
}
Expand Down Expand Up @@ -292,6 +294,15 @@ class RNMBXPackage : TurboReactPackage() {
false,
isTurboModule
)
moduleInfos[RNMBXChangeLineOffsetsShapeAnimatorModule.NAME] = ReactModuleInfo(
RNMBXChangeLineOffsetsShapeAnimatorModule.NAME,
RNMBXChangeLineOffsetsShapeAnimatorModule.NAME,
false,
false,
false,
false,
isTurboModule
)
moduleInfos
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.EventDispatcher
import com.rnmapbox.rnmbx.events.IEvent
import com.rnmapbox.rnmbx.utils.Logger

/**
* Created by nickitaliano on 8/23/17.
Expand Down Expand Up @@ -40,14 +41,19 @@ abstract class AbstractEventEmitter<T : ViewGroup?>(reactApplicationContext: Rea
return
}
mRateLimitedEvents[eventCacheKey] = System.currentTimeMillis()
mEventDispatcher!!.dispatchEvent(
AbstractEvent(
event.iD,
event.key,
event.canCoalesce(),
event.toJSON()

try {
mEventDispatcher!!.dispatchEvent(
AbstractEvent(
event.iD,
event.key,
event.canCoalesce(),
event.toJSON()
)
)
)
} catch (e: Exception) {
Logger.e("Error dispatching event:", e.toString())
}
}

override fun addEventEmitters(context: ThemedReactContext, view: T) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
package com.rnmapbox.rnmbx.components.styles.sources

import android.content.Context
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.rnmapbox.rnmbx.utils.ImageEntry
import android.graphics.drawable.BitmapDrawable
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReadableMap
import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView
import com.rnmapbox.rnmbx.events.FeatureClickEvent
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.mapbox.bindgen.Value
import com.mapbox.geojson.Feature
import com.rnmapbox.rnmbx.events.AndroidCallbackEvent
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.GeoJson
import com.mapbox.geojson.Geometry
import com.mapbox.maps.*
import com.mapbox.maps.extension.style.expressions.generated.Expression
import com.rnmapbox.rnmbx.shape_animators.ShapeAnimationConsumer
import com.rnmapbox.rnmbx.shape_animators.ShapeAnimator
import com.rnmapbox.rnmbx.shape_animators.ShapeAnimatorManager
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.rnmapbox.rnmbx.components.RemovalReason
import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView
import com.rnmapbox.rnmbx.events.AndroidCallbackEvent
import com.rnmapbox.rnmbx.events.FeatureClickEvent
import com.rnmapbox.rnmbx.shapeAnimators.ShapeAnimationConsumer
import com.rnmapbox.rnmbx.shapeAnimators.ShapeAnimator
import com.rnmapbox.rnmbx.utils.Logger
import com.rnmapbox.rnmbx.v11compat.feature.*
import java.net.URL
import java.util.ArrayList
import java.util.HashMap

import com.rnmapbox.rnmbx.v11compat.feature.*
private const val LOG_TAG = "RNMBXShapeSource"

class RNMBXShapeSource(context: Context, private val mManager: RNMBXShapeSourceManager) :
RNMBXSource<GeoJsonSource>(context), ShapeAnimationConsumer {
Expand All @@ -47,13 +43,22 @@ class RNMBXShapeSource(context: Context, private val mManager: RNMBXShapeSourceM
}

override fun addToMap(mapView: RNMBXMapView) {

// Wait for style before adding the source to the map
mapView.getMapboxMap().getStyle {
val map = mapView.getMapboxMap()
super@RNMBXShapeSource.addToMap(mapView)
}
}

override fun removeFromMap(mapView: RNMBXMapView, reason: RemovalReason): Boolean {

if (reason == RemovalReason.VIEW_REMOVAL) {
mShapeAnimator?.unsubscribe(this)
}
return super.removeFromMap(mapView, reason)
}

override fun setId(id: Int) {
super.setId(id)
mManager.tagAssigned(id)
Expand Down Expand Up @@ -85,7 +90,8 @@ class RNMBXShapeSource(context: Context, private val mManager: RNMBXShapeSourceM
mShapeAnimator = shapeAnimator
shapeAnimator.subscribe(this)

shapeUpdated(shapeAnimator.getShape())
val shape = shapeAnimator.getShape()
shapeUpdated(shape)
}
} else {
mShape = geoJSONStr
Expand All @@ -108,8 +114,8 @@ class RNMBXShapeSource(context: Context, private val mManager: RNMBXShapeSourceM
else -> {
Logger.e(
LOG_TAG,
"Cannot convert shape to GeoJSONSourceData, neitthe Geometry, nor Feature or FeatureCollection: $geoJson"
);
"Cannot convert shape to Geometry, Feature, or FeatureCollection: $geoJson"
)
return null
}
}
Expand Down Expand Up @@ -214,7 +220,7 @@ class RNMBXShapeSource(context: Context, private val mManager: RNMBXShapeSourceM
)
) { features ->
if (features.isError) {
Logger.e("RNMBXShapeSource", String.format("Error: %s", features.error))
Logger.e(LOG_TAG, String.format("Error: %s", features.error))
} else {
val payload: WritableMap = WritableNativeMap()
val result: MutableList<Feature> = ArrayList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ import com.rnmapbox.rnmbx.components.AbstractEventEmitter
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.bridge.ReadableType
import com.facebook.react.common.MapBuilder
import com.facebook.react.viewmanagers.RNMBXShapeSourceManagerInterface
import com.mapbox.bindgen.Value
import com.mapbox.maps.extension.style.expressions.generated.Expression
import com.rnmapbox.rnmbx.events.constants.EventKeys
import com.rnmapbox.rnmbx.events.constants.eventMapOf
import com.rnmapbox.rnmbx.shape_animators.ShapeAnimatorManager
import com.rnmapbox.rnmbx.shapeAnimators.ShapeAnimatorManager
import com.rnmapbox.rnmbx.utils.ExpressionParser
import com.rnmapbox.rnmbx.utils.Logger
import com.rnmapbox.rnmbx.utils.ViewTagResolver
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.rnmapbox.rnmbx.shapeAnimators

internal class AnimatableElement<T>(
var source: T,
var progress: T,
var target: T,
var startedAtSec: Double,
var progressDurationSec: Double,
var totalDurationSec: Double,
/** A function returning the difference in meters between the two values. */
var getDistanceRemaining: (a: T, b: T) -> Double
) {
fun distanceRemaining(): Double {
return getDistanceRemaining(source, target)
}

fun durationRatio(): Double {
return if (totalDurationSec > 0.0) {
progressDurationSec / totalDurationSec
} else {
1.0
}
}

fun setProgress(value: T, animatorAgeSec: Double) {
progress = value
progressDurationSec = (animatorAgeSec - startedAtSec)
}

fun reset(_source: T, _progress: T, _target: T, durationSec: Double, animatorAgeSec: Double) {
this.source = _source
this.progress = _progress
this.target = _target
this.startedAtSec = animatorAgeSec
this.progressDurationSec = 0.0
this.totalDurationSec = durationSec
}
}
Loading

0 comments on commit c5d94e6

Please sign in to comment.