@rafaelaugusto/jolt 0.1.0-beta.9 → 0.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -21
- package/dist/animate.d.ts +25 -4
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +67 -26
- package/dist/animate.js.map +1 -1
- package/dist/blur/index.d.ts +6 -0
- package/dist/blur/index.d.ts.map +1 -0
- package/dist/blur/index.js +196 -0
- package/dist/blur/index.js.map +1 -0
- package/dist/blur/native.d.ts +17 -0
- package/dist/blur/native.d.ts.map +1 -0
- package/dist/blur/native.js +44 -0
- package/dist/blur/native.js.map +1 -0
- package/dist/blur/types.d.ts +32 -0
- package/dist/blur/types.d.ts.map +1 -0
- package/dist/blur/types.js +2 -0
- package/dist/blur/types.js.map +1 -0
- package/dist/bottom-sheet/geometry.d.ts +18 -0
- package/dist/bottom-sheet/geometry.d.ts.map +1 -0
- package/dist/bottom-sheet/geometry.js +51 -0
- package/dist/bottom-sheet/geometry.js.map +1 -0
- package/dist/bottom-sheet/index.d.ts +56 -0
- package/dist/bottom-sheet/index.d.ts.map +1 -0
- package/dist/bottom-sheet/index.js +462 -0
- package/dist/bottom-sheet/index.js.map +1 -0
- package/dist/bottom-sheet/native.d.ts +11 -0
- package/dist/bottom-sheet/native.d.ts.map +1 -0
- package/dist/bottom-sheet/native.js +173 -0
- package/dist/bottom-sheet/native.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -1
- package/dist/config.js.map +1 -1
- package/dist/decay.d.ts +1 -0
- package/dist/decay.d.ts.map +1 -1
- package/dist/decay.js +7 -0
- package/dist/decay.js.map +1 -1
- package/dist/drag-physics.d.ts +6 -1
- package/dist/drag-physics.d.ts.map +1 -1
- package/dist/drag.d.ts +17 -3
- package/dist/drag.d.ts.map +1 -1
- package/dist/drag.js +34 -11
- package/dist/drag.js.map +1 -1
- package/dist/driver/android-gradient.d.ts +5 -0
- package/dist/driver/android-gradient.d.ts.map +1 -0
- package/dist/driver/android-gradient.js +99 -0
- package/dist/driver/android-gradient.js.map +1 -0
- package/dist/driver/android-particles.d.ts +6 -0
- package/dist/driver/android-particles.d.ts.map +1 -0
- package/dist/driver/android-particles.js +93 -0
- package/dist/driver/android-particles.js.map +1 -0
- package/dist/driver/android.d.ts.map +1 -1
- package/dist/driver/android.js +27 -1
- package/dist/driver/android.js.map +1 -1
- package/dist/driver/frame.d.ts +3 -0
- package/dist/driver/frame.d.ts.map +1 -0
- package/dist/driver/frame.js +7 -0
- package/dist/driver/frame.js.map +1 -0
- package/dist/driver/index.d.ts +4 -0
- package/dist/driver/index.d.ts.map +1 -1
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/ios-gradient.d.ts +5 -0
- package/dist/driver/ios-gradient.d.ts.map +1 -0
- package/dist/driver/ios-gradient.js +131 -0
- package/dist/driver/ios-gradient.js.map +1 -0
- package/dist/driver/ios-scroll-delegate.d.ts +4 -0
- package/dist/driver/ios-scroll-delegate.d.ts.map +1 -0
- package/dist/driver/ios-scroll-delegate.js +26 -0
- package/dist/driver/ios-scroll-delegate.js.map +1 -0
- package/dist/driver/ios.d.ts.map +1 -1
- package/dist/driver/ios.js +9 -0
- package/dist/driver/ios.js.map +1 -1
- package/dist/easing.d.ts +3 -1
- package/dist/easing.d.ts.map +1 -1
- package/dist/easing.js +18 -1
- package/dist/easing.js.map +1 -1
- package/dist/engine.d.ts +31 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +194 -42
- package/dist/engine.js.map +1 -1
- package/dist/gesture-native.d.ts +28 -0
- package/dist/gesture-native.d.ts.map +1 -0
- package/dist/gesture-native.js +61 -0
- package/dist/gesture-native.js.map +1 -0
- package/dist/gestures/index.d.ts +69 -0
- package/dist/gestures/index.d.ts.map +1 -0
- package/dist/gestures/index.js +262 -0
- package/dist/gestures/index.js.map +1 -0
- package/dist/glass/index.d.ts +55 -0
- package/dist/glass/index.d.ts.map +1 -0
- package/dist/glass/index.js +205 -0
- package/dist/glass/index.js.map +1 -0
- package/dist/gradient-binding.d.ts +38 -0
- package/dist/gradient-binding.d.ts.map +1 -0
- package/dist/gradient-binding.js +128 -0
- package/dist/gradient-binding.js.map +1 -0
- package/dist/gradient.d.ts +105 -0
- package/dist/gradient.d.ts.map +1 -0
- package/dist/gradient.js +514 -0
- package/dist/gradient.js.map +1 -0
- package/dist/gravity.d.ts +13 -0
- package/dist/gravity.d.ts.map +1 -0
- package/dist/gravity.js +84 -0
- package/dist/gravity.js.map +1 -0
- package/dist/haptics.d.ts.map +1 -1
- package/dist/haptics.js +101 -28
- package/dist/haptics.js.map +1 -1
- package/dist/image-effect.d.ts +1 -0
- package/dist/image-effect.d.ts.map +1 -1
- package/dist/image-effect.js +10 -2
- package/dist/image-effect.js.map +1 -1
- package/dist/index.d.ts +19 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/inspector/index.d.ts +86 -0
- package/dist/inspector/index.d.ts.map +1 -0
- package/dist/inspector/index.js +419 -0
- package/dist/inspector/index.js.map +1 -0
- package/dist/inspector/plot.d.ts +4 -0
- package/dist/inspector/plot.d.ts.map +1 -0
- package/dist/inspector/plot.js +23 -0
- package/dist/inspector/plot.js.map +1 -0
- package/dist/inspector/probe.d.ts +19 -0
- package/dist/inspector/probe.d.ts.map +1 -0
- package/dist/inspector/probe.js +37 -0
- package/dist/inspector/probe.js.map +1 -0
- package/dist/inspector/tuning.d.ts +19 -0
- package/dist/inspector/tuning.d.ts.map +1 -0
- package/dist/inspector/tuning.js +37 -0
- package/dist/inspector/tuning.js.map +1 -0
- package/dist/interactive-navigation/index.d.ts +44 -0
- package/dist/interactive-navigation/index.d.ts.map +1 -0
- package/dist/interactive-navigation/index.js +350 -0
- package/dist/interactive-navigation/index.js.map +1 -0
- package/dist/keyboard.d.ts +24 -0
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js +199 -6
- package/dist/keyboard.js.map +1 -1
- package/dist/live-spring-driver.d.ts +52 -0
- package/dist/live-spring-driver.d.ts.map +1 -0
- package/dist/live-spring-driver.js +104 -0
- package/dist/live-spring-driver.js.map +1 -0
- package/dist/mixer.d.ts +55 -0
- package/dist/mixer.d.ts.map +1 -0
- package/dist/mixer.js +286 -0
- package/dist/mixer.js.map +1 -0
- package/dist/motion-scope.d.ts +8 -2
- package/dist/motion-scope.d.ts.map +1 -1
- package/dist/motion-scope.js +24 -3
- package/dist/motion-scope.js.map +1 -1
- package/dist/motion-states.d.ts +19 -7
- package/dist/motion-states.d.ts.map +1 -1
- package/dist/motion-states.js +116 -26
- package/dist/motion-states.js.map +1 -1
- package/dist/motion-value.d.ts +227 -0
- package/dist/motion-value.d.ts.map +1 -0
- package/dist/motion-value.js +1074 -0
- package/dist/motion-value.js.map +1 -0
- package/dist/native/index.d.ts +10 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +6 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native-properties.d.ts +28 -0
- package/dist/native-properties.d.ts.map +1 -0
- package/dist/native-properties.js +154 -0
- package/dist/native-properties.js.map +1 -0
- package/dist/native-property-driver.d.ts +19 -0
- package/dist/native-property-driver.d.ts.map +1 -0
- package/dist/native-property-driver.js +69 -0
- package/dist/native-property-driver.js.map +1 -0
- package/dist/native-property.d.ts +37 -0
- package/dist/native-property.d.ts.map +1 -0
- package/dist/native-property.js +285 -0
- package/dist/native-property.js.map +1 -0
- package/dist/particles.d.ts +5 -2
- package/dist/particles.d.ts.map +1 -1
- package/dist/particles.js +16 -10
- package/dist/particles.js.map +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +2 -2
- package/dist/path.js.map +1 -1
- package/dist/physics/index.d.ts +139 -0
- package/dist/physics/index.d.ts.map +1 -0
- package/dist/physics/index.js +316 -0
- package/dist/physics/index.js.map +1 -0
- package/dist/physics/native.d.ts +54 -0
- package/dist/physics/native.d.ts.map +1 -0
- package/dist/physics/native.js +64 -0
- package/dist/physics/native.js.map +1 -0
- package/dist/playback-group.d.ts.map +1 -1
- package/dist/playback-group.js +17 -1
- package/dist/playback-group.js.map +1 -1
- package/dist/playback.d.ts +15 -0
- package/dist/playback.d.ts.map +1 -1
- package/dist/playback.js +23 -0
- package/dist/playback.js.map +1 -1
- package/dist/predictive-back/index.d.ts +45 -0
- package/dist/predictive-back/index.d.ts.map +1 -0
- package/dist/predictive-back/index.js +220 -0
- package/dist/predictive-back/index.js.map +1 -0
- package/dist/predictive-back/presentation.d.ts +4 -0
- package/dist/predictive-back/presentation.d.ts.map +1 -0
- package/dist/predictive-back/presentation.js +3 -0
- package/dist/predictive-back/presentation.js.map +1 -0
- package/dist/press.d.ts.map +1 -1
- package/dist/press.js +11 -2
- package/dist/press.js.map +1 -1
- package/dist/reorder/handle.d.ts +6 -0
- package/dist/reorder/handle.d.ts.map +1 -0
- package/dist/reorder/handle.js +10 -0
- package/dist/reorder/handle.js.map +1 -0
- package/dist/reorder/index.d.ts +10 -0
- package/dist/reorder/index.d.ts.map +1 -0
- package/dist/reorder/index.js +233 -0
- package/dist/reorder/index.js.map +1 -0
- package/dist/reorder/list-view.d.ts +5 -0
- package/dist/reorder/list-view.d.ts.map +1 -0
- package/dist/reorder/list-view.js +238 -0
- package/dist/reorder/list-view.js.map +1 -0
- package/dist/reorder/model.d.ts +6 -0
- package/dist/reorder/model.d.ts.map +1 -0
- package/dist/reorder/model.js +30 -0
- package/dist/reorder/model.js.map +1 -0
- package/dist/reorder/surface.d.ts +6 -0
- package/dist/reorder/surface.d.ts.map +1 -0
- package/dist/reorder/surface.js +10 -0
- package/dist/reorder/surface.js.map +1 -0
- package/dist/reorder/types.d.ts +92 -0
- package/dist/reorder/types.d.ts.map +1 -0
- package/dist/reorder/types.js +2 -0
- package/dist/reorder/types.js.map +1 -0
- package/dist/sampling.d.ts +35 -0
- package/dist/sampling.d.ts.map +1 -0
- package/dist/sampling.js +123 -0
- package/dist/sampling.js.map +1 -0
- package/dist/scene-appearance.d.ts +14 -0
- package/dist/scene-appearance.d.ts.map +1 -0
- package/dist/scene-appearance.js +68 -0
- package/dist/scene-appearance.js.map +1 -0
- package/dist/scene-camera.d.ts +5 -0
- package/dist/scene-camera.d.ts.map +1 -0
- package/dist/scene-camera.js +23 -0
- package/dist/scene-camera.js.map +1 -0
- package/dist/scene-shader.d.ts +18 -0
- package/dist/scene-shader.d.ts.map +1 -0
- package/dist/scene-shader.js +76 -0
- package/dist/scene-shader.js.map +1 -0
- package/dist/scroll-header/geometry.d.ts +18 -0
- package/dist/scroll-header/geometry.d.ts.map +1 -0
- package/dist/scroll-header/geometry.js +46 -0
- package/dist/scroll-header/geometry.js.map +1 -0
- package/dist/scroll-header/index.d.ts +48 -0
- package/dist/scroll-header/index.d.ts.map +1 -0
- package/dist/scroll-header/index.js +399 -0
- package/dist/scroll-header/index.js.map +1 -0
- package/dist/scroll-header/native.d.ts +28 -0
- package/dist/scroll-header/native.d.ts.map +1 -0
- package/dist/scroll-header/native.js +43 -0
- package/dist/scroll-header/native.js.map +1 -0
- package/dist/sensors/index.d.ts +69 -0
- package/dist/sensors/index.d.ts.map +1 -0
- package/dist/sensors/index.js +149 -0
- package/dist/sensors/index.js.map +1 -0
- package/dist/sensors/native.d.ts +20 -0
- package/dist/sensors/native.d.ts.map +1 -0
- package/dist/sensors/native.js +48 -0
- package/dist/sensors/native.js.map +1 -0
- package/dist/sequence.d.ts +24 -0
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +181 -19
- package/dist/sequence.js.map +1 -1
- package/dist/shapes/android.d.ts +3 -0
- package/dist/shapes/android.d.ts.map +1 -0
- package/dist/shapes/android.js +51 -0
- package/dist/shapes/android.js.map +1 -0
- package/dist/shapes/index.d.ts +6 -0
- package/dist/shapes/index.d.ts.map +1 -0
- package/dist/shapes/index.js +238 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/shapes/ios.d.ts +3 -0
- package/dist/shapes/ios.d.ts.map +1 -0
- package/dist/shapes/ios.js +164 -0
- package/dist/shapes/ios.js.map +1 -0
- package/dist/shapes/native.d.ts +18 -0
- package/dist/shapes/native.d.ts.map +1 -0
- package/dist/shapes/native.js +10 -0
- package/dist/shapes/native.js.map +1 -0
- package/dist/shapes/path.d.ts +10 -0
- package/dist/shapes/path.d.ts.map +1 -0
- package/dist/shapes/path.js +106 -0
- package/dist/shapes/path.js.map +1 -0
- package/dist/shapes/types.d.ts +55 -0
- package/dist/shapes/types.d.ts.map +1 -0
- package/dist/shapes/types.js +11 -0
- package/dist/shapes/types.js.map +1 -0
- package/dist/shared-layout/index.d.ts +40 -0
- package/dist/shared-layout/index.d.ts.map +1 -0
- package/dist/shared-layout/index.js +267 -0
- package/dist/shared-layout/index.js.map +1 -0
- package/dist/shared-layout/native.d.ts +15 -0
- package/dist/shared-layout/native.d.ts.map +1 -0
- package/dist/shared-layout/native.js +79 -0
- package/dist/shared-layout/native.js.map +1 -0
- package/dist/shared-layout/timing.d.ts +9 -0
- package/dist/shared-layout/timing.d.ts.map +1 -0
- package/dist/shared-layout/timing.js +68 -0
- package/dist/shared-layout/timing.js.map +1 -0
- package/dist/shared-navigation/entry.d.ts +51 -0
- package/dist/shared-navigation/entry.d.ts.map +1 -0
- package/dist/shared-navigation/entry.js +619 -0
- package/dist/shared-navigation/entry.js.map +1 -0
- package/dist/shared-navigation/index.d.ts +58 -0
- package/dist/shared-navigation/index.d.ts.map +1 -0
- package/dist/shared-navigation/index.js +318 -0
- package/dist/shared-navigation/index.js.map +1 -0
- package/dist/shared-navigation/return-signal.d.ts +16 -0
- package/dist/shared-navigation/return-signal.d.ts.map +1 -0
- package/dist/shared-navigation/return-signal.js +35 -0
- package/dist/shared-navigation/return-signal.js.map +1 -0
- package/dist/shared-navigation/return-target.d.ts +32 -0
- package/dist/shared-navigation/return-target.d.ts.map +1 -0
- package/dist/shared-navigation/return-target.js +168 -0
- package/dist/shared-navigation/return-target.js.map +1 -0
- package/dist/shared-navigation/scope.d.ts +4 -0
- package/dist/shared-navigation/scope.d.ts.map +1 -0
- package/dist/shared-navigation/scope.js +2 -0
- package/dist/shared-navigation/scope.js.map +1 -0
- package/dist/shared-navigation/targets.d.ts +6 -0
- package/dist/shared-navigation/targets.d.ts.map +1 -0
- package/dist/shared-navigation/targets.js +36 -0
- package/dist/shared-navigation/targets.js.map +1 -0
- package/dist/size-layout.d.ts +19 -0
- package/dist/size-layout.d.ts.map +1 -0
- package/dist/size-layout.js +216 -0
- package/dist/size-layout.js.map +1 -0
- package/dist/size.d.ts +19 -0
- package/dist/size.d.ts.map +1 -0
- package/dist/size.js +251 -0
- package/dist/size.js.map +1 -0
- package/dist/spring.d.ts +2 -0
- package/dist/spring.d.ts.map +1 -1
- package/dist/spring.js +11 -0
- package/dist/spring.js.map +1 -1
- package/dist/svg-path.d.ts +18 -0
- package/dist/svg-path.d.ts.map +1 -1
- package/dist/svg-path.js +3 -2
- package/dist/svg-path.js.map +1 -1
- package/dist/testing/clock.d.ts +40 -0
- package/dist/testing/clock.d.ts.map +1 -0
- package/dist/testing/clock.js +116 -0
- package/dist/testing/clock.js.map +1 -0
- package/dist/testing/handoff-recorder.d.ts +8 -0
- package/dist/testing/handoff-recorder.d.ts.map +1 -0
- package/dist/testing/handoff-recorder.js +77 -0
- package/dist/testing/handoff-recorder.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/native.d.ts +46 -0
- package/dist/testing/native.d.ts.map +1 -0
- package/dist/testing/native.js +313 -0
- package/dist/testing/native.js.map +1 -0
- package/dist/testing/presentation.d.ts +11 -0
- package/dist/testing/presentation.d.ts.map +1 -0
- package/dist/testing/presentation.js +45 -0
- package/dist/testing/presentation.js.map +1 -0
- package/dist/testing/replay.d.ts +39 -0
- package/dist/testing/replay.d.ts.map +1 -0
- package/dist/testing/replay.js +99 -0
- package/dist/testing/replay.js.map +1 -0
- package/dist/testing/scroll-handoff-native.d.ts +10 -0
- package/dist/testing/scroll-handoff-native.d.ts.map +1 -0
- package/dist/testing/scroll-handoff-native.js +151 -0
- package/dist/testing/scroll-handoff-native.js.map +1 -0
- package/dist/testing/scroll-handoff.d.ts +25 -0
- package/dist/testing/scroll-handoff.d.ts.map +1 -0
- package/dist/testing/scroll-handoff.js +127 -0
- package/dist/testing/scroll-handoff.js.map +1 -0
- package/dist/testing/sensors.d.ts +13 -0
- package/dist/testing/sensors.d.ts.map +1 -0
- package/dist/testing/sensors.js +26 -0
- package/dist/testing/sensors.js.map +1 -0
- package/dist/testing/trace.d.ts +101 -0
- package/dist/testing/trace.d.ts.map +1 -0
- package/dist/testing/trace.js +263 -0
- package/dist/testing/trace.js.map +1 -0
- package/dist/timed-animation.d.ts +38 -0
- package/dist/timed-animation.d.ts.map +1 -0
- package/dist/timed-animation.js +177 -0
- package/dist/timed-animation.js.map +1 -0
- package/dist/track.d.ts +3 -0
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +281 -40
- package/dist/track.js.map +1 -1
- package/dist/transition.d.ts +54 -2
- package/dist/transition.d.ts.map +1 -1
- package/dist/transition.js +128 -8
- package/dist/transition.js.map +1 -1
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/value-observers.d.ts +22 -0
- package/dist/value-observers.d.ts.map +1 -0
- package/dist/value-observers.js +122 -0
- package/dist/value-observers.js.map +1 -0
- package/dist/value-track.d.ts +13 -0
- package/dist/value-track.d.ts.map +1 -0
- package/dist/value-track.js +59 -0
- package/dist/value-track.js.map +1 -0
- package/dist/vue/index.d.ts +33 -3
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +118 -3
- package/dist/vue/index.js.map +1 -1
- package/dist/zoomable.d.ts +1 -1
- package/dist/zoomable.d.ts.map +1 -1
- package/package.json +68 -3
- package/platforms/android/include.gradle +10 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/BlurEffect.java +89 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/CircleMath.java +158 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/CircleWorld.java +221 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/GestureSession.java +227 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/GestureSurface.java +54 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/GradientLayer.java +224 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrame.java +53 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFrameFollower.java +57 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardObserver.java +97 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/LiveSpring.java +277 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/MotionProbe.java +70 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ParticleEmitter.java +192 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ParticleSimulation.java +114 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/PredictiveBackSession.java +212 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ReorderHandle.java +25 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSession.java +213 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ReorderSurface.java +27 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ScrollHeader.java +84 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/SensorMath.java +28 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/SensorSource.java +217 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ShapeLayer.java +120 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/SharedLayoutFlight.java +287 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/SharedNavigationScene.java +115 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/SpringState.java +39 -0
- package/platforms/android/proguard-rules.pro +20 -0
- package/platforms/ios/build.xcconfig +1 -1
- package/platforms/ios/src/JoltBlur.h +14 -0
- package/platforms/ios/src/JoltBlur.m +86 -0
- package/platforms/ios/src/JoltCircleMath.h +191 -0
- package/platforms/ios/src/JoltCircleWorld.h +18 -0
- package/platforms/ios/src/JoltCircleWorld.m +218 -0
- package/platforms/ios/src/JoltGestureSession.h +23 -0
- package/platforms/ios/src/JoltGestureSession.m +251 -0
- package/platforms/ios/src/JoltInteractiveNavigation.h +23 -0
- package/platforms/ios/src/JoltInteractiveNavigation.m +255 -0
- package/platforms/ios/src/JoltLiveSpring.h +29 -0
- package/platforms/ios/src/JoltLiveSpring.m +243 -0
- package/platforms/ios/src/JoltMotionProbe.h +9 -0
- package/platforms/ios/src/JoltMotionProbe.m +72 -0
- package/platforms/ios/src/JoltReorderSession.h +15 -0
- package/platforms/ios/src/JoltReorderSession.m +205 -0
- package/platforms/ios/src/JoltScrollHeader.h +13 -0
- package/platforms/ios/src/JoltScrollHeader.m +101 -0
- package/platforms/ios/src/JoltSensorMath.h +39 -0
- package/platforms/ios/src/JoltSensorSource.h +13 -0
- package/platforms/ios/src/JoltSensorSource.m +201 -0
- package/platforms/ios/src/JoltSharedLayoutFlight.h +19 -0
- package/platforms/ios/src/JoltSharedLayoutFlight.m +250 -0
- package/platforms/ios/src/JoltSpringState.h +35 -0
- package/platforms/ios/src/module.modulemap +10 -0
- package/src/android-plugin.d.ts +464 -0
- package/src/animate.ts +129 -15
- package/src/blur/index.ts +195 -0
- package/src/blur/native.ts +70 -0
- package/src/blur/types.ts +32 -0
- package/src/bottom-sheet/geometry.ts +84 -0
- package/src/bottom-sheet/index.ts +536 -0
- package/src/bottom-sheet/native.ts +214 -0
- package/src/config.ts +39 -1
- package/src/decay.ts +9 -0
- package/src/drag.ts +67 -21
- package/src/driver/android-gradient.ts +116 -0
- package/src/driver/android-particles.ts +118 -0
- package/src/driver/android.ts +31 -1
- package/src/driver/frame.ts +10 -0
- package/src/driver/index.ts +4 -0
- package/src/driver/ios-gradient.ts +136 -0
- package/src/driver/ios-scroll-delegate.ts +23 -0
- package/src/driver/ios.ts +10 -0
- package/src/easing.ts +21 -1
- package/src/engine.ts +209 -60
- package/src/gesture-native.ts +139 -0
- package/src/gestures/index.ts +365 -0
- package/src/glass/index.ts +218 -0
- package/src/gradient-binding.ts +158 -0
- package/src/gradient.ts +663 -0
- package/src/gravity.ts +107 -0
- package/src/haptics.ts +106 -28
- package/src/image-effect.ts +10 -2
- package/src/index.ts +56 -7
- package/src/inspector/index.ts +523 -0
- package/src/inspector/plot.ts +27 -0
- package/src/inspector/probe.ts +50 -0
- package/src/inspector/tuning.ts +51 -0
- package/src/interactive-navigation/index.ts +427 -0
- package/src/ios-plugin.d.ts +280 -0
- package/src/keyboard.ts +231 -7
- package/src/live-spring-driver.ts +181 -0
- package/src/mixer.ts +312 -0
- package/src/motion-scope.ts +39 -6
- package/src/motion-states.ts +149 -42
- package/src/motion-value.ts +1289 -0
- package/src/native/index.ts +30 -0
- package/src/native-properties.ts +188 -0
- package/src/native-property-driver.ts +123 -0
- package/src/native-property.ts +366 -0
- package/src/particles.ts +18 -17
- package/src/path.ts +2 -1
- package/src/physics/index.ts +476 -0
- package/src/physics/native.ts +121 -0
- package/src/playback-group.ts +17 -1
- package/src/playback.ts +41 -0
- package/src/predictive-back/index.ts +280 -0
- package/src/predictive-back/presentation.ts +6 -0
- package/src/press.ts +10 -2
- package/src/reorder/handle.ts +9 -0
- package/src/reorder/index.ts +243 -0
- package/src/reorder/list-view.ts +262 -0
- package/src/reorder/model.ts +47 -0
- package/src/reorder/surface.ts +10 -0
- package/src/reorder/types.ts +87 -0
- package/src/sampling.ts +173 -0
- package/src/scene-appearance.ts +92 -0
- package/src/scene-camera.ts +30 -0
- package/src/scene-shader.ts +139 -0
- package/src/scroll-header/geometry.ts +88 -0
- package/src/scroll-header/index.ts +530 -0
- package/src/scroll-header/native.ts +89 -0
- package/src/sensors/index.ts +246 -0
- package/src/sensors/native.ts +72 -0
- package/src/sequence.ts +209 -20
- package/src/shapes/android.ts +63 -0
- package/src/shapes/index.ts +267 -0
- package/src/shapes/ios.ts +194 -0
- package/src/shapes/native.ts +27 -0
- package/src/shapes/path.ts +127 -0
- package/src/shapes/types.ts +63 -0
- package/src/shared-layout/index.ts +343 -0
- package/src/shared-layout/native.ts +138 -0
- package/src/shared-layout/timing.ts +87 -0
- package/src/shared-navigation/entry.ts +695 -0
- package/src/shared-navigation/index.ts +414 -0
- package/src/shared-navigation/return-signal.ts +41 -0
- package/src/shared-navigation/return-target.ts +197 -0
- package/src/shared-navigation/scope.ts +3 -0
- package/src/shared-navigation/targets.ts +38 -0
- package/src/size-layout.ts +237 -0
- package/src/size.ts +302 -0
- package/src/spring.ts +18 -0
- package/src/svg-path.ts +8 -3
- package/src/testing/clock.ts +128 -0
- package/src/testing/handoff-recorder.ts +98 -0
- package/src/testing/index.ts +4 -0
- package/src/testing/native.ts +386 -0
- package/src/testing/presentation.ts +46 -0
- package/src/testing/replay.ts +149 -0
- package/src/testing/scroll-handoff-native.ts +201 -0
- package/src/testing/scroll-handoff.ts +140 -0
- package/src/testing/sensors.ts +41 -0
- package/src/testing/trace.ts +340 -0
- package/src/timed-animation.ts +192 -0
- package/src/track.ts +334 -38
- package/src/transition.ts +197 -9
- package/src/types.ts +11 -1
- package/src/value-observers.ts +147 -0
- package/src/value-track.ts +71 -0
- package/src/vue/index.ts +147 -1
- package/src/zoomable.ts +1 -1
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Application,
|
|
3
|
+
Color,
|
|
4
|
+
ScrollView,
|
|
5
|
+
type EventData,
|
|
6
|
+
type ScrollEventData,
|
|
7
|
+
type View,
|
|
8
|
+
} from '@nativescript/core'
|
|
9
|
+
import { toViews } from './animate.js'
|
|
10
|
+
import {
|
|
11
|
+
colorMixer,
|
|
12
|
+
isColorProperty,
|
|
13
|
+
parseColor,
|
|
14
|
+
validateColorTarget,
|
|
15
|
+
type ColorValue,
|
|
16
|
+
} from './color.js'
|
|
17
|
+
import { onReducedMotion, reducedMotion, timeScale } from './config.js'
|
|
18
|
+
import { springPhysics, type SpringParams } from './spring.js'
|
|
19
|
+
import { mixFor } from './mixer.js'
|
|
20
|
+
import {
|
|
21
|
+
liveSpringDriver,
|
|
22
|
+
syncLiveSprings,
|
|
23
|
+
type LiveNode,
|
|
24
|
+
type LiveMapping,
|
|
25
|
+
type LiveBinding,
|
|
26
|
+
} from './live-spring-driver.js'
|
|
27
|
+
import {
|
|
28
|
+
clampDecoration,
|
|
29
|
+
isDecorationProperty,
|
|
30
|
+
validateDecorationTarget,
|
|
31
|
+
} from './decoration.js'
|
|
32
|
+
import { getDriver } from './driver/index.js'
|
|
33
|
+
import { setBase, stopBase, ViewAnimation, type Animation, type Plan } from './engine.js'
|
|
34
|
+
import { linear } from './easing.js'
|
|
35
|
+
import { isProperty, type NumericProperty, type Property } from './properties.js'
|
|
36
|
+
import { createTrack, instantTrack, type Track } from './track.js'
|
|
37
|
+
import {
|
|
38
|
+
resolveTransition,
|
|
39
|
+
springs,
|
|
40
|
+
type ResolvedTransition,
|
|
41
|
+
type SpringName,
|
|
42
|
+
type Transition,
|
|
43
|
+
} from './transition.js'
|
|
44
|
+
import type { KeyframeProperty, Target } from './types.js'
|
|
45
|
+
import { playbackSource, type PlaybackAnimation, type PlaybackClock } from './playback.js'
|
|
46
|
+
import { TimedAnimation } from './timed-animation.js'
|
|
47
|
+
import { valueTrack } from './value-track.js'
|
|
48
|
+
import {
|
|
49
|
+
ValueObservers,
|
|
50
|
+
type SubscribeOptions,
|
|
51
|
+
type ValueListener,
|
|
52
|
+
} from './value-observers.js'
|
|
53
|
+
|
|
54
|
+
export interface MapOptions {
|
|
55
|
+
/** Hold the first and last outputs outside the input range. Defaults to `true`. */
|
|
56
|
+
clamp?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Named motion value layers add scalar offsets before derived mappings. */
|
|
60
|
+
export type ValueAnimationOptions = Transition & { layer?: string }
|
|
61
|
+
|
|
62
|
+
declare const motionValueBrand: unique symbol
|
|
63
|
+
|
|
64
|
+
/** A number derived from a motion value, ready to bind to a view. */
|
|
65
|
+
export interface DerivedValue {
|
|
66
|
+
/** The value now, while it animates too. */
|
|
67
|
+
get(): number
|
|
68
|
+
/** Observe changes in JavaScript on the native frame clock. Unsubscribe when finished. */
|
|
69
|
+
subscribe(listener: ValueListener<number>, options?: SubscribeOptions): () => void
|
|
70
|
+
/** Maps through ascending or descending input ranges, like React Native's `interpolate`. */
|
|
71
|
+
to(
|
|
72
|
+
input: readonly number[],
|
|
73
|
+
output: readonly number[],
|
|
74
|
+
options?: MapOptions,
|
|
75
|
+
): DerivedValue
|
|
76
|
+
to(input: readonly number[], output: readonly ColorValue[]): DerivedColor
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A color derived from a motion value, for `backgroundColor`, `borderColor` or `color`. */
|
|
80
|
+
export interface DerivedColor {
|
|
81
|
+
get(): Color
|
|
82
|
+
/** Observe mapped color changes in JavaScript on the native frame clock. */
|
|
83
|
+
subscribe(listener: ValueListener<Color>, options?: SubscribeOptions): () => void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** A number that animates natively and drives every view property bound to it. */
|
|
87
|
+
export interface MotionValue extends DerivedValue {
|
|
88
|
+
readonly [motionValueBrand]: true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
type ColorKey = 'backgroundColor' | 'borderColor' | 'color'
|
|
92
|
+
export type Bindings = {
|
|
93
|
+
[K in Exclude<KeyframeProperty, ColorKey>]?: DerivedValue
|
|
94
|
+
} & { [K in ColorKey]?: DerivedColor }
|
|
95
|
+
|
|
96
|
+
interface Binding {
|
|
97
|
+
readonly view: View
|
|
98
|
+
readonly property: Property
|
|
99
|
+
readonly root: Root
|
|
100
|
+
readonly source?: Derived
|
|
101
|
+
readonly read: (value: number) => number
|
|
102
|
+
animation?: ViewAnimation
|
|
103
|
+
cleanup?: () => void
|
|
104
|
+
sync(): void
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface Run {
|
|
108
|
+
readonly track: Track
|
|
109
|
+
readonly base: number
|
|
110
|
+
readonly span: number
|
|
111
|
+
readonly animation: TimedAnimation
|
|
112
|
+
readonly startedAt: number
|
|
113
|
+
readonly clock?: Pick<PlaybackClock, 'time'>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type Presentation = Pick<Run, 'track' | 'base' | 'span' | 'startedAt' | 'clock'>
|
|
117
|
+
|
|
118
|
+
const bound = new WeakMap<View, Map<Property, Binding>>()
|
|
119
|
+
|
|
120
|
+
class Derived {
|
|
121
|
+
constructor(
|
|
122
|
+
private readonly source: Root | undefined,
|
|
123
|
+
readonly read: (value: number) => number,
|
|
124
|
+
readonly color: boolean,
|
|
125
|
+
readonly mappings: readonly LiveMapping[] = [],
|
|
126
|
+
) {}
|
|
127
|
+
|
|
128
|
+
get root(): Root {
|
|
129
|
+
return this.source ?? (this as unknown as Root)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get(): number | Color {
|
|
133
|
+
const value = this.read(this.root.raw())
|
|
134
|
+
return this.color ? new Color(value) : value
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
subscribe(listener: ValueListener<number | Color>, options?: SubscribeOptions) {
|
|
138
|
+
const release = this.root.retainConsumer()
|
|
139
|
+
try {
|
|
140
|
+
const stop = this.root.observers.subscribe(this.read, this.color, listener, options)
|
|
141
|
+
return () => {
|
|
142
|
+
stop()
|
|
143
|
+
release()
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
release()
|
|
147
|
+
throw error
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
to(
|
|
152
|
+
input: readonly number[],
|
|
153
|
+
output: readonly (number | ColorValue)[],
|
|
154
|
+
options?: MapOptions,
|
|
155
|
+
): Derived {
|
|
156
|
+
if (this.color) throw new TypeError('A color cannot be mapped again.')
|
|
157
|
+
const map = mapping(input, output, options)
|
|
158
|
+
if (this.root instanceof SpringRoot && map.color)
|
|
159
|
+
throw new TypeError(
|
|
160
|
+
'Live spring bindings currently support numeric transforms and opacity.',
|
|
161
|
+
)
|
|
162
|
+
const read = this.read
|
|
163
|
+
return new Derived(
|
|
164
|
+
this.root,
|
|
165
|
+
(value) => map.read(read(value)),
|
|
166
|
+
map.color,
|
|
167
|
+
map.color
|
|
168
|
+
? []
|
|
169
|
+
: [
|
|
170
|
+
...this.mappings,
|
|
171
|
+
{
|
|
172
|
+
input: [...map.input],
|
|
173
|
+
output: [...map.numbers],
|
|
174
|
+
clamp: options?.clamp !== false,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
class Root extends Derived {
|
|
182
|
+
private consumers = 0
|
|
183
|
+
readonly demandListeners = new Set<(active: boolean) => void>()
|
|
184
|
+
get demanded() {
|
|
185
|
+
return this.consumers > 0
|
|
186
|
+
}
|
|
187
|
+
retainConsumer(): () => void {
|
|
188
|
+
this.consumers++
|
|
189
|
+
try {
|
|
190
|
+
this.demandChanged()
|
|
191
|
+
} catch (error) {
|
|
192
|
+
this.consumers--
|
|
193
|
+
this.demandChanged()
|
|
194
|
+
throw error
|
|
195
|
+
}
|
|
196
|
+
let ended = false
|
|
197
|
+
return () => {
|
|
198
|
+
if (ended) return
|
|
199
|
+
ended = true
|
|
200
|
+
this.consumers--
|
|
201
|
+
this.demandChanged()
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
protected demandChanged() {
|
|
205
|
+
for (const listener of [...this.demandListeners]) listener(this.demanded)
|
|
206
|
+
}
|
|
207
|
+
readonly bindings = new Set<Binding>()
|
|
208
|
+
readonly listeners = new Set<() => void>()
|
|
209
|
+
readonly observers = new ValueObservers(
|
|
210
|
+
(time) => this.raw(time),
|
|
211
|
+
() => this.running,
|
|
212
|
+
)
|
|
213
|
+
private readonly rests = new Map<string, number>()
|
|
214
|
+
private readonly runs = new Map<string, Run>()
|
|
215
|
+
private presentation: Presentation | undefined
|
|
216
|
+
private cancelPresentation: (() => void) | undefined
|
|
217
|
+
|
|
218
|
+
constructor(initial: number) {
|
|
219
|
+
super(undefined, (value) => value, false)
|
|
220
|
+
if (!Number.isFinite(initial))
|
|
221
|
+
throw new RangeError(`A motion value starts from a finite number, got ${initial}.`)
|
|
222
|
+
this.rests.set('', initial)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
raw(time = getDriver().now()): number {
|
|
226
|
+
let result = 0
|
|
227
|
+
for (const key of new Set([...this.rests.keys(), ...this.runs.keys()]))
|
|
228
|
+
result += this.part(key, time)
|
|
229
|
+
return result
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Drag input owns only the base; bounds and snap points stay in base units. */
|
|
233
|
+
base(): number {
|
|
234
|
+
return this.part('')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private part(key: string, time = getDriver().now()): number {
|
|
238
|
+
const run = this.runs.get(key)
|
|
239
|
+
return run
|
|
240
|
+
? run.base + run.span * run.track.valueAt(this.runTime(run, time))
|
|
241
|
+
: (this.rests.get(key) ?? 0)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private runTime(run: Presentation, time = getDriver().now()) {
|
|
245
|
+
return run.clock?.time() ?? time - run.startedAt
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Internal development snapshot; reading it installs no observers. */
|
|
249
|
+
inspect() {
|
|
250
|
+
return {
|
|
251
|
+
velocity: [...this.runs.keys()].reduce((sum, key) => sum + this.velocity(key), 0),
|
|
252
|
+
resources:
|
|
253
|
+
this.bindings.size + this.listeners.size + this.observers.size + this.runs.size,
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private velocity(key: string): number {
|
|
258
|
+
const run = this.runs.get(key)
|
|
259
|
+
if (!run || run.animation.paused) return 0
|
|
260
|
+
return run.span * run.track.velocityAt(this.runTime(run))
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
animate(
|
|
264
|
+
target: number | readonly (number | null)[],
|
|
265
|
+
transition: ValueAnimationOptions | SpringName = {},
|
|
266
|
+
): PlaybackAnimation {
|
|
267
|
+
const options: ValueAnimationOptions =
|
|
268
|
+
typeof transition === 'string' ? springOf(transition) : transition
|
|
269
|
+
const key = options.layer === undefined ? '' : layerName(options.layer)
|
|
270
|
+
const from = this.part(key)
|
|
271
|
+
const frames = (typeof target === 'number' ? [null, target] : [...target]).map(
|
|
272
|
+
(frame) => (frame === null ? from : frame),
|
|
273
|
+
)
|
|
274
|
+
if (frames.length < 2) frames.unshift(from)
|
|
275
|
+
if (frames.some((frame) => typeof frame !== 'number' || !Number.isFinite(frame)))
|
|
276
|
+
throw new RangeError(`Invalid motion value keyframes: ${JSON.stringify(target)}.`)
|
|
277
|
+
const resolved = resolveTransition(options, 'x', frames.length)
|
|
278
|
+
const base = Math.min(...frames)
|
|
279
|
+
const span = Math.max(...frames) - base
|
|
280
|
+
const velocity = this.velocity(key)
|
|
281
|
+
if (resolved.curve.type === 'gravity') {
|
|
282
|
+
// Acceleration and velocity belong to the value's units, not normalized progress.
|
|
283
|
+
const track = createTrack('x', frames, resolved, velocity)
|
|
284
|
+
this.halt(key)
|
|
285
|
+
return track.end === 0 ? this.jump(track.to, key) : this.start(track, 0, 1, key)
|
|
286
|
+
}
|
|
287
|
+
if (span === 0) {
|
|
288
|
+
this.halt(key)
|
|
289
|
+
return this.jump(frames.at(-1)!, key)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Sampled in the value's own range so springs settle relative to the distance.
|
|
293
|
+
const track = createTrack(
|
|
294
|
+
'opacity',
|
|
295
|
+
frames.map((frame) => (frame - base) / span),
|
|
296
|
+
resolved,
|
|
297
|
+
velocity / span,
|
|
298
|
+
)
|
|
299
|
+
this.halt(key)
|
|
300
|
+
return track.end === 0
|
|
301
|
+
? this.jump(base + span * track.to, key)
|
|
302
|
+
: this.start(track, base, span, key)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Plays a track sampled in the value's own units, such as a drag release. */
|
|
306
|
+
playTrack(track: Track): PlaybackAnimation {
|
|
307
|
+
this.halt('')
|
|
308
|
+
return track.end === 0 ? this.jump(track.to, '') : this.start(track, 0, 1, '')
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private jump(value: number, key: string): PlaybackAnimation {
|
|
312
|
+
this.rests.set(key, value)
|
|
313
|
+
this.sync()
|
|
314
|
+
const animation = new TimedAnimation(
|
|
315
|
+
0,
|
|
316
|
+
0,
|
|
317
|
+
() => {},
|
|
318
|
+
() => {},
|
|
319
|
+
)
|
|
320
|
+
animation.start()
|
|
321
|
+
return animation
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private start(
|
|
325
|
+
track: Track,
|
|
326
|
+
base: number,
|
|
327
|
+
span: number,
|
|
328
|
+
key: string,
|
|
329
|
+
): PlaybackAnimation {
|
|
330
|
+
const driver = getDriver()
|
|
331
|
+
const animation = new TimedAnimation(
|
|
332
|
+
track.end,
|
|
333
|
+
track.arrival,
|
|
334
|
+
() => this.sync(),
|
|
335
|
+
(completed) => {
|
|
336
|
+
if (this.runs.get(key)?.animation !== animation) return
|
|
337
|
+
const held = base + span * track.valueAt(animation.time)
|
|
338
|
+
this.runs.delete(key)
|
|
339
|
+
if (!completed && key !== '') this.rests.delete(key)
|
|
340
|
+
else this.rests.set(key, completed ? base + span * track.to : held)
|
|
341
|
+
this.sync()
|
|
342
|
+
},
|
|
343
|
+
)
|
|
344
|
+
const run: Run = {
|
|
345
|
+
track,
|
|
346
|
+
base,
|
|
347
|
+
span,
|
|
348
|
+
animation,
|
|
349
|
+
startedAt: driver.now(),
|
|
350
|
+
clock: { time: () => animation.time },
|
|
351
|
+
}
|
|
352
|
+
this.runs.set(key, run)
|
|
353
|
+
this.sync()
|
|
354
|
+
animation.start()
|
|
355
|
+
return animation
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
set(value: number) {
|
|
359
|
+
if (!Number.isFinite(value))
|
|
360
|
+
throw new RangeError(`A motion value takes a finite number, got ${value}.`)
|
|
361
|
+
for (const key of [...this.runs.keys()]) this.halt(key)
|
|
362
|
+
this.rests.clear()
|
|
363
|
+
this.rests.set('', value)
|
|
364
|
+
this.sync()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
setBase(value: number) {
|
|
368
|
+
if (!Number.isFinite(value))
|
|
369
|
+
throw new RangeError('A motion value takes a finite number.')
|
|
370
|
+
this.halt('')
|
|
371
|
+
this.rests.set('', value)
|
|
372
|
+
this.sync()
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
stopBase() {
|
|
376
|
+
this.stopChannel('')
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
stop(layer?: string) {
|
|
380
|
+
if (layer !== undefined) return this.stopChannel(layerName(layer))
|
|
381
|
+
if (!this.runs.size && this.rests.size === 1) return
|
|
382
|
+
const value = this.raw()
|
|
383
|
+
for (const key of [...this.runs.keys()]) this.halt(key)
|
|
384
|
+
this.rests.clear()
|
|
385
|
+
this.rests.set('', value)
|
|
386
|
+
this.sync()
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private stopChannel(key: string) {
|
|
390
|
+
if (!this.runs.has(key) && (key === '' || !this.rests.has(key))) return
|
|
391
|
+
const held = this.part(key)
|
|
392
|
+
this.halt(key)
|
|
393
|
+
if (key === '') this.rests.set(key, held)
|
|
394
|
+
else this.rests.delete(key)
|
|
395
|
+
this.sync()
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private sync() {
|
|
399
|
+
this.cancelPresentation?.()
|
|
400
|
+
this.cancelPresentation = undefined
|
|
401
|
+
const now = getDriver().now()
|
|
402
|
+
let rest = 0
|
|
403
|
+
for (const [key, value] of this.rests) if (!this.runs.has(key)) rest += value
|
|
404
|
+
const runs = [...this.runs.values()].filter((run) => !run.animation.paused)
|
|
405
|
+
for (const [key, run] of this.runs)
|
|
406
|
+
if (run.animation.paused) rest += this.part(key, now)
|
|
407
|
+
if (!runs.length) this.presentation = undefined
|
|
408
|
+
else if (runs.length === 1) {
|
|
409
|
+
const run = runs[0]!
|
|
410
|
+
this.presentation = { ...run, base: run.base + rest }
|
|
411
|
+
} else {
|
|
412
|
+
const { track, windowed } = valueTrack(
|
|
413
|
+
runs.map((run) => ({
|
|
414
|
+
track: run.track,
|
|
415
|
+
base: run.base,
|
|
416
|
+
span: run.span,
|
|
417
|
+
elapsed: this.runTime(run, now),
|
|
418
|
+
})),
|
|
419
|
+
rest,
|
|
420
|
+
)
|
|
421
|
+
const driver = getDriver()
|
|
422
|
+
const clock = driver.clock?.(track.end)
|
|
423
|
+
this.presentation = { track, base: 0, span: 1, startedAt: now, clock }
|
|
424
|
+
const cancelWindow = windowed
|
|
425
|
+
? driver.after(track.end, () => this.sync())
|
|
426
|
+
: undefined
|
|
427
|
+
this.cancelPresentation = () => {
|
|
428
|
+
cancelWindow?.()
|
|
429
|
+
clock?.cancel()
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
this.changed()
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
protected changed() {
|
|
436
|
+
for (const binding of this.bindings) binding.sync()
|
|
437
|
+
for (const listener of [...this.listeners]) listener()
|
|
438
|
+
this.observers.changed(this.running)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** The plan that plays this value's run, mapped, on one bound property. */
|
|
442
|
+
plan(binding: Binding): Plan {
|
|
443
|
+
const run = this.presentation
|
|
444
|
+
const { property, read } = binding
|
|
445
|
+
const write = (value: number) =>
|
|
446
|
+
isDecorationProperty(property)
|
|
447
|
+
? clampDecoration(property, read(value))
|
|
448
|
+
: read(value)
|
|
449
|
+
if (!run) {
|
|
450
|
+
const value = write(this.raw())
|
|
451
|
+
return {
|
|
452
|
+
property,
|
|
453
|
+
keyframes: [],
|
|
454
|
+
transition: still,
|
|
455
|
+
track: () => instantTrack(property, value),
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const { track, base, span } = run
|
|
459
|
+
const at = (progress: number) => write(base + span * progress)
|
|
460
|
+
const transition: ResolvedTransition = {
|
|
461
|
+
curve: {
|
|
462
|
+
type: 'tween',
|
|
463
|
+
duration: track.duration,
|
|
464
|
+
ease: [linear],
|
|
465
|
+
times: undefined,
|
|
466
|
+
},
|
|
467
|
+
delay: track.delay,
|
|
468
|
+
repeat: track.repeat,
|
|
469
|
+
reverse: track.reverse,
|
|
470
|
+
}
|
|
471
|
+
const mapped = (): Track => {
|
|
472
|
+
if (reducedMotion() && property !== 'opacity' && !isColorProperty(property))
|
|
473
|
+
return instantTrack(property, at(track.to))
|
|
474
|
+
return {
|
|
475
|
+
property,
|
|
476
|
+
delay: track.delay,
|
|
477
|
+
duration: track.duration,
|
|
478
|
+
repeat: track.repeat,
|
|
479
|
+
reverse: track.reverse,
|
|
480
|
+
times: track.times,
|
|
481
|
+
values: track.values.map(at),
|
|
482
|
+
to: at(track.to),
|
|
483
|
+
end: track.end,
|
|
484
|
+
arrival: track.arrival,
|
|
485
|
+
valueAt: (elapsed) => at(track.valueAt(elapsed)),
|
|
486
|
+
velocityAt: isColorProperty(property)
|
|
487
|
+
? () => 0
|
|
488
|
+
: (elapsed) =>
|
|
489
|
+
(at(track.valueAt(elapsed + 0.5)) - at(track.valueAt(elapsed - 0.5))) *
|
|
490
|
+
1000,
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return { property, keyframes: [], transition, track: mapped }
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Capture the current native presentation clock, even if a later command replaces it. */
|
|
497
|
+
captureTime(): () => number {
|
|
498
|
+
const run = this.presentation
|
|
499
|
+
return run ? () => this.runTime(run) : () => 0
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
elapsed(): number {
|
|
503
|
+
return this.presentation ? this.runTime(this.presentation) : 0
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
get paused(): boolean {
|
|
507
|
+
return this.runs.size > 0 && !this.running
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
get running(): boolean {
|
|
511
|
+
return [...this.runs.values()].some((run) => !run.animation.paused)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
private halt(key: string) {
|
|
515
|
+
const run = this.runs.get(key)
|
|
516
|
+
if (!run) return
|
|
517
|
+
this.runs.delete(key)
|
|
518
|
+
run.animation.stop()
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export interface SpringValueOptions extends SpringParams {
|
|
523
|
+
/** Initial follower position. Defaults to the source's current value. */
|
|
524
|
+
initial?: number
|
|
525
|
+
/** Absolute source units. Defaults to 0.001. */
|
|
526
|
+
restDelta?: number
|
|
527
|
+
/** Source units per second. Defaults to 0.01. */
|
|
528
|
+
restSpeed?: number
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** A retained native follower. Settling does not dispose its source subscription. */
|
|
532
|
+
export interface SpringValue extends DerivedValue {
|
|
533
|
+
readonly disposed: boolean
|
|
534
|
+
readonly moving: boolean
|
|
535
|
+
readonly paused: boolean
|
|
536
|
+
/** Hold position and velocity while retaining the current source target. */
|
|
537
|
+
pause(): void
|
|
538
|
+
/** Resume from the held state toward the current source. */
|
|
539
|
+
play(): void
|
|
540
|
+
/** Current velocity in source units per second. */
|
|
541
|
+
getVelocity(): number
|
|
542
|
+
dispose(): void
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
class SpringRoot extends Root {
|
|
546
|
+
private releaseDemand: (() => void) | undefined
|
|
547
|
+
externalActive = false
|
|
548
|
+
protected override demandChanged() {
|
|
549
|
+
const active = this.demanded && !this.ended && !this.held && !this.background
|
|
550
|
+
if (active && !this.releaseDemand) {
|
|
551
|
+
// A source status callback may synchronously suspend or dispose this follower.
|
|
552
|
+
this.releaseDemand = () => {}
|
|
553
|
+
let release: () => void
|
|
554
|
+
try {
|
|
555
|
+
release = this.input.root.retainConsumer()
|
|
556
|
+
} catch (error) {
|
|
557
|
+
this.releaseDemand = undefined
|
|
558
|
+
throw error
|
|
559
|
+
}
|
|
560
|
+
this.releaseDemand = release
|
|
561
|
+
if (!this.demanded || this.ended || this.held || this.background) {
|
|
562
|
+
this.releaseDemand = undefined
|
|
563
|
+
release()
|
|
564
|
+
}
|
|
565
|
+
} else if (!active && this.releaseDemand) {
|
|
566
|
+
const release = this.releaseDemand
|
|
567
|
+
this.releaseDemand = undefined
|
|
568
|
+
release()
|
|
569
|
+
}
|
|
570
|
+
super.demandChanged()
|
|
571
|
+
}
|
|
572
|
+
readonly node: LiveNode
|
|
573
|
+
private releaseSource: () => void = () => {}
|
|
574
|
+
private releaseReduced: () => void = () => {}
|
|
575
|
+
private ended = false
|
|
576
|
+
private held = false
|
|
577
|
+
private background = false
|
|
578
|
+
private suspend = () => {
|
|
579
|
+
this.background = true
|
|
580
|
+
this.updatePause()
|
|
581
|
+
}
|
|
582
|
+
private resume = () => {
|
|
583
|
+
this.background = false
|
|
584
|
+
this.updatePause()
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
constructor(
|
|
588
|
+
readonly input: Derived,
|
|
589
|
+
options: SpringValueOptions,
|
|
590
|
+
) {
|
|
591
|
+
const source = input
|
|
592
|
+
const initial = options.initial ?? (source.get() as number)
|
|
593
|
+
const instant = options.duration === 0
|
|
594
|
+
const params = springPhysics(instant ? { ...options, duration: undefined } : options)
|
|
595
|
+
const speed = timeScale()
|
|
596
|
+
const physics = {
|
|
597
|
+
stiffness: params.stiffness * speed * speed,
|
|
598
|
+
damping: params.damping * speed,
|
|
599
|
+
mass: params.mass,
|
|
600
|
+
restDelta: options.restDelta ?? 0.001,
|
|
601
|
+
restSpeed: options.restSpeed ?? 0.01,
|
|
602
|
+
}
|
|
603
|
+
for (const [key, value] of Object.entries(physics))
|
|
604
|
+
if (!Number.isFinite(value) || (key === 'damping' ? value < 0 : value <= 0))
|
|
605
|
+
throw new RangeError(`Invalid springValue ${key}: ${value}.`)
|
|
606
|
+
if (
|
|
607
|
+
options.duration !== undefined &&
|
|
608
|
+
(!Number.isFinite(options.duration) || options.duration < 0)
|
|
609
|
+
)
|
|
610
|
+
throw new RangeError('springValue duration must be finite and nonnegative.')
|
|
611
|
+
if (options.bounce !== undefined && !Number.isFinite(options.bounce))
|
|
612
|
+
throw new RangeError('springValue bounce must be finite.')
|
|
613
|
+
super(initial)
|
|
614
|
+
this.node = liveSpringDriver().create(initial, physics, instant || reducedMotion())
|
|
615
|
+
const root = source.root
|
|
616
|
+
try {
|
|
617
|
+
let update: () => void
|
|
618
|
+
if (root instanceof SpringRoot) {
|
|
619
|
+
if (root.disposed) throw new Error('Cannot follow a disposed spring value.')
|
|
620
|
+
this.node.follow(root.node, source.mappings)
|
|
621
|
+
update = () => this.notify()
|
|
622
|
+
} else {
|
|
623
|
+
const playback = valuePlayback(source as unknown as DerivedValue)
|
|
624
|
+
update = () => {
|
|
625
|
+
if (!root.running) {
|
|
626
|
+
this.node.update({
|
|
627
|
+
value: source.get() as number,
|
|
628
|
+
running: false,
|
|
629
|
+
elapsed: 0,
|
|
630
|
+
duration: 0,
|
|
631
|
+
delay: 0,
|
|
632
|
+
repeat: 0,
|
|
633
|
+
reverse: false,
|
|
634
|
+
to: 0,
|
|
635
|
+
times: [],
|
|
636
|
+
values: [],
|
|
637
|
+
})
|
|
638
|
+
this.notify()
|
|
639
|
+
return
|
|
640
|
+
}
|
|
641
|
+
const { track, elapsed, running, value } = playback.snapshot()
|
|
642
|
+
this.node.update({
|
|
643
|
+
value,
|
|
644
|
+
elapsed,
|
|
645
|
+
running,
|
|
646
|
+
duration: track.duration,
|
|
647
|
+
delay: track.delay,
|
|
648
|
+
repeat: Number.isFinite(track.repeat) ? track.repeat : -1,
|
|
649
|
+
reverse: track.reverse,
|
|
650
|
+
to: track.to,
|
|
651
|
+
times: track.times,
|
|
652
|
+
values: track.values,
|
|
653
|
+
})
|
|
654
|
+
this.notify()
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
root.listeners.add(update)
|
|
658
|
+
this.releaseSource = () => {
|
|
659
|
+
root.listeners.delete(update)
|
|
660
|
+
}
|
|
661
|
+
this.releaseReduced = onReducedMotion((reduced) => {
|
|
662
|
+
this.node.reduced(instant || reduced)
|
|
663
|
+
this.notify()
|
|
664
|
+
})
|
|
665
|
+
Application.on(Application.suspendEvent, this.suspend)
|
|
666
|
+
Application.on(Application.resumeEvent, this.resume)
|
|
667
|
+
update()
|
|
668
|
+
} catch (error) {
|
|
669
|
+
this.dispose()
|
|
670
|
+
throw error
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
override raw(): number {
|
|
674
|
+
return this.node?.position() ?? super.raw()
|
|
675
|
+
}
|
|
676
|
+
override get running(): boolean {
|
|
677
|
+
return (
|
|
678
|
+
!this.ended &&
|
|
679
|
+
!this.held &&
|
|
680
|
+
!this.background &&
|
|
681
|
+
(this.externalActive || (this.node?.moving() ?? false))
|
|
682
|
+
)
|
|
683
|
+
}
|
|
684
|
+
get moving(): boolean {
|
|
685
|
+
return this.running
|
|
686
|
+
}
|
|
687
|
+
get disposed(): boolean {
|
|
688
|
+
return this.ended
|
|
689
|
+
}
|
|
690
|
+
override get paused(): boolean {
|
|
691
|
+
return this.held
|
|
692
|
+
}
|
|
693
|
+
pause() {
|
|
694
|
+
if (!this.ended) {
|
|
695
|
+
this.held = true
|
|
696
|
+
this.updatePause()
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
play() {
|
|
700
|
+
if (!this.ended) {
|
|
701
|
+
this.held = false
|
|
702
|
+
this.updatePause()
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
private updatePause() {
|
|
706
|
+
this.node.paused(this.held || this.background)
|
|
707
|
+
this.demandChanged()
|
|
708
|
+
this.notify()
|
|
709
|
+
}
|
|
710
|
+
getVelocity(): number {
|
|
711
|
+
return this.ended ? 0 : this.node.velocity()
|
|
712
|
+
}
|
|
713
|
+
override plan(): Plan {
|
|
714
|
+
throw new TypeError(
|
|
715
|
+
'A live spring cannot be sampled as a fixed track. Bind it to transforms or opacity.',
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
notify() {
|
|
719
|
+
this.observers.changed(this.running)
|
|
720
|
+
for (const listener of [...this.listeners]) listener()
|
|
721
|
+
}
|
|
722
|
+
dispose() {
|
|
723
|
+
if (this.ended) return
|
|
724
|
+
this.ended = true
|
|
725
|
+
this.demandChanged()
|
|
726
|
+
this.releaseSource()
|
|
727
|
+
this.releaseReduced()
|
|
728
|
+
Application.off(Application.suspendEvent, this.suspend)
|
|
729
|
+
Application.off(Application.resumeEvent, this.resume)
|
|
730
|
+
for (const binding of [...this.bindings]) release(binding)
|
|
731
|
+
this.node.dispose()
|
|
732
|
+
this.observers.clear()
|
|
733
|
+
for (const listener of [...this.listeners]) listener()
|
|
734
|
+
this.listeners.clear()
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** Follow changing targets without rebuilding sampled animation tracks. Dispose with the owning scope. */
|
|
739
|
+
export function springValue(
|
|
740
|
+
source: DerivedValue,
|
|
741
|
+
options: SpringValueOptions = {},
|
|
742
|
+
): SpringValue {
|
|
743
|
+
if (!(source instanceof Derived) || source.color)
|
|
744
|
+
throw new TypeError('springValue() needs a numeric Jolt motion value.')
|
|
745
|
+
if (!options || typeof options !== 'object')
|
|
746
|
+
throw new TypeError('springValue() options must be an object.')
|
|
747
|
+
return new SpringRoot(source, options) as unknown as SpringValue
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/** Internal boundary notification for values driven directly by native input. */
|
|
751
|
+
export function liveValueInput(value: DerivedValue) {
|
|
752
|
+
if (!(value instanceof Derived) || !(value.root instanceof SpringRoot))
|
|
753
|
+
throw new TypeError('Native input requires a live value.')
|
|
754
|
+
const root = value.root
|
|
755
|
+
return {
|
|
756
|
+
node: root.node,
|
|
757
|
+
changed: () => root.notify(),
|
|
758
|
+
active(value: boolean) {
|
|
759
|
+
root.externalActive = value
|
|
760
|
+
root.notify()
|
|
761
|
+
},
|
|
762
|
+
demand(listener: (active: boolean) => void) {
|
|
763
|
+
root.demandListeners.add(listener)
|
|
764
|
+
listener(root.demanded)
|
|
765
|
+
return () => {
|
|
766
|
+
root.demandListeners.delete(listener)
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function layerName(name: string): string {
|
|
773
|
+
if (typeof name !== 'string' || !name.length)
|
|
774
|
+
throw new TypeError('A layer is named by a nonempty string.')
|
|
775
|
+
return name
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const still: ResolvedTransition = {
|
|
779
|
+
curve: { type: 'tween', duration: 0, ease: [linear], times: undefined },
|
|
780
|
+
delay: 0,
|
|
781
|
+
repeat: 0,
|
|
782
|
+
reverse: false,
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* A number that animates natively and drives the view properties bound to it.
|
|
787
|
+
*
|
|
788
|
+
* ```ts
|
|
789
|
+
* const progress = motionValue(0)
|
|
790
|
+
* bind(sheet, { y: progress.to([0, 1], [600, 0]) })
|
|
791
|
+
* animate(progress, 1, 'snappy')
|
|
792
|
+
* ```
|
|
793
|
+
*/
|
|
794
|
+
export function motionValue(initial = 0): MotionValue {
|
|
795
|
+
return new Root(initial) as unknown as MotionValue
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function isMotionValue(target: unknown): target is MotionValue {
|
|
799
|
+
return target instanceof Root && !(target instanceof SpringRoot)
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
export function asRoot(value: MotionValue): Root {
|
|
803
|
+
return value as unknown as Root
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Lets motion values drive a view's properties. The view takes their current values
|
|
808
|
+
* at once and follows every change on the same native clock. Returns a cleanup function.
|
|
809
|
+
*
|
|
810
|
+
* ```ts
|
|
811
|
+
* const release = bind(backdrop, { opacity: progress.to([0, 1], [0, 0.4]) })
|
|
812
|
+
* ```
|
|
813
|
+
*/
|
|
814
|
+
export function bind(target: Target | readonly Target[], bindings: Bindings): () => void {
|
|
815
|
+
if (!bindings || typeof bindings !== 'object' || Array.isArray(bindings))
|
|
816
|
+
throw new TypeError('bind() takes an object of motion values.')
|
|
817
|
+
const views = toViews(target)
|
|
818
|
+
const entries: [Property, Derived][] = []
|
|
819
|
+
for (const [key, source] of Object.entries(bindings)) {
|
|
820
|
+
if (source === undefined) continue
|
|
821
|
+
if (!(source instanceof Derived))
|
|
822
|
+
throw new TypeError(`"${key}" needs a motion value, got ${String(source)}.`)
|
|
823
|
+
const properties: Property[] =
|
|
824
|
+
key === 'scale' ? ['scaleX', 'scaleY'] : isProperty(key) ? [key] : []
|
|
825
|
+
if (!properties.length) throw new Error(`Can't bind "${key}".`)
|
|
826
|
+
for (const property of properties) {
|
|
827
|
+
if (source.root instanceof SpringRoot) {
|
|
828
|
+
if (source.root.disposed) throw new Error('Cannot bind a disposed spring value.')
|
|
829
|
+
if (isColorProperty(property) || isDecorationProperty(property))
|
|
830
|
+
throw new TypeError(
|
|
831
|
+
'Live spring bindings currently support numeric transforms and opacity.',
|
|
832
|
+
)
|
|
833
|
+
}
|
|
834
|
+
if (isColorProperty(property) !== source.color)
|
|
835
|
+
throw new TypeError(
|
|
836
|
+
source.color
|
|
837
|
+
? `"${key}" takes numbers; map the value to numbers.`
|
|
838
|
+
: `"${key}" is a color; map the value to colors with to(input, colors).`,
|
|
839
|
+
)
|
|
840
|
+
entries.push([property, source])
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
for (const view of views)
|
|
844
|
+
for (const [property] of entries)
|
|
845
|
+
if (isColorProperty(property)) validateColorTarget(view, property)
|
|
846
|
+
else if (isDecorationProperty(property)) validateDecorationTarget(view, property)
|
|
847
|
+
|
|
848
|
+
const made: (() => void)[] = []
|
|
849
|
+
try {
|
|
850
|
+
for (const view of views)
|
|
851
|
+
for (const [property, source] of entries) made.push(attach(view, property, source))
|
|
852
|
+
} catch (error) {
|
|
853
|
+
for (const cleanup of made.reverse()) cleanup()
|
|
854
|
+
throw error
|
|
855
|
+
}
|
|
856
|
+
return () => made.forEach((release) => release())
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
function attach(view: View, property: Property, source: Derived) {
|
|
860
|
+
const existing = bound.get(view)?.get(property)
|
|
861
|
+
if (existing) release(existing)
|
|
862
|
+
const root = source.root
|
|
863
|
+
if (root instanceof SpringRoot)
|
|
864
|
+
return attachSpring(view, property as NumericProperty, source, root)
|
|
865
|
+
const binding: Binding = {
|
|
866
|
+
view,
|
|
867
|
+
property,
|
|
868
|
+
root,
|
|
869
|
+
source,
|
|
870
|
+
read: source.read,
|
|
871
|
+
sync() {
|
|
872
|
+
if (!view.isLoaded || !view.nativeViewProtected) return
|
|
873
|
+
if (!root.running) {
|
|
874
|
+
// A value at rest writes straight through, so scroll offsets cost one write per event.
|
|
875
|
+
binding.animation?.stop()
|
|
876
|
+
binding.animation = undefined
|
|
877
|
+
stopBase(view, [property])
|
|
878
|
+
const value = source.read(root.raw())
|
|
879
|
+
setBase(
|
|
880
|
+
view,
|
|
881
|
+
property,
|
|
882
|
+
isDecorationProperty(property) ? clampDecoration(property, value) : value,
|
|
883
|
+
)
|
|
884
|
+
if (isColorProperty(property) || isDecorationProperty(property))
|
|
885
|
+
getDriver().sync?.(view)
|
|
886
|
+
return
|
|
887
|
+
}
|
|
888
|
+
const animation = new ViewAnimation(view, [root.plan(binding)])
|
|
889
|
+
// Native keyframe setup can take time, especially for a composite track on Android.
|
|
890
|
+
// Catch up after construction so each binding joins the source's current phase.
|
|
891
|
+
const elapsed = root.elapsed()
|
|
892
|
+
if (root.paused) animation.pause()
|
|
893
|
+
if ((elapsed > 1 || root.paused) && animation.duration > 0)
|
|
894
|
+
animation.seekTime(elapsed)
|
|
895
|
+
binding.animation = animation
|
|
896
|
+
},
|
|
897
|
+
}
|
|
898
|
+
let properties = bound.get(view)
|
|
899
|
+
if (!properties) bound.set(view, (properties = new Map()))
|
|
900
|
+
properties.set(property, binding)
|
|
901
|
+
root.bindings.add(binding)
|
|
902
|
+
view.on('loaded', binding.sync)
|
|
903
|
+
try {
|
|
904
|
+
binding.sync()
|
|
905
|
+
} catch (error) {
|
|
906
|
+
release(binding)
|
|
907
|
+
throw error
|
|
908
|
+
}
|
|
909
|
+
return () => {
|
|
910
|
+
if (root.bindings.has(binding)) {
|
|
911
|
+
release(binding)
|
|
912
|
+
binding.animation?.stop()
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function attachSpring(
|
|
918
|
+
view: View,
|
|
919
|
+
property: NumericProperty,
|
|
920
|
+
source: Derived,
|
|
921
|
+
root: SpringRoot,
|
|
922
|
+
) {
|
|
923
|
+
let native: LiveBinding | undefined
|
|
924
|
+
let releaseMix: (() => void) | undefined
|
|
925
|
+
let releaseDemand: (() => void) | undefined
|
|
926
|
+
const detach = () => {
|
|
927
|
+
if (!native) return
|
|
928
|
+
native.cancel()
|
|
929
|
+
releaseMix?.()
|
|
930
|
+
releaseMix = undefined
|
|
931
|
+
native = undefined
|
|
932
|
+
releaseDemand?.()
|
|
933
|
+
releaseDemand = undefined
|
|
934
|
+
syncLiveSprings(view)
|
|
935
|
+
}
|
|
936
|
+
const binding: Binding = {
|
|
937
|
+
view,
|
|
938
|
+
property,
|
|
939
|
+
root,
|
|
940
|
+
source,
|
|
941
|
+
read: source.read,
|
|
942
|
+
sync() {
|
|
943
|
+
if (!view.isLoaded || !view.nativeViewProtected || root.disposed) return
|
|
944
|
+
detach()
|
|
945
|
+
stopBase(view, [property])
|
|
946
|
+
native = root.node.bind(view, property, source.mappings)
|
|
947
|
+
const handle = native
|
|
948
|
+
const retained = root.retainConsumer()
|
|
949
|
+
if (native !== handle || root.disposed) {
|
|
950
|
+
retained()
|
|
951
|
+
return
|
|
952
|
+
}
|
|
953
|
+
releaseDemand = retained
|
|
954
|
+
releaseMix = mixFor(view, property).attachLive({
|
|
955
|
+
read: () => handle.value(),
|
|
956
|
+
layers: (layers) => handle.layers(layers),
|
|
957
|
+
})
|
|
958
|
+
syncLiveSprings(view)
|
|
959
|
+
},
|
|
960
|
+
cleanup() {
|
|
961
|
+
view.off('unloaded', detach)
|
|
962
|
+
detach()
|
|
963
|
+
},
|
|
964
|
+
}
|
|
965
|
+
let properties = bound.get(view)
|
|
966
|
+
if (!properties) bound.set(view, (properties = new Map()))
|
|
967
|
+
properties.set(property, binding)
|
|
968
|
+
root.bindings.add(binding)
|
|
969
|
+
view.on('loaded', binding.sync)
|
|
970
|
+
view.on('unloaded', detach)
|
|
971
|
+
try {
|
|
972
|
+
binding.sync()
|
|
973
|
+
} catch (error) {
|
|
974
|
+
release(binding)
|
|
975
|
+
throw error
|
|
976
|
+
}
|
|
977
|
+
return () => {
|
|
978
|
+
if (root.bindings.has(binding)) release(binding)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/** A read-only value on an existing finite animation's clock. No second animation or timer. */
|
|
983
|
+
export function playbackProgress(animation: Animation): DerivedValue {
|
|
984
|
+
const source = playbackSource(animation)
|
|
985
|
+
if (!Number.isFinite(source.read().duration))
|
|
986
|
+
throw new RangeError('Progress requires a finite animation.')
|
|
987
|
+
const root = new PlaybackValue(source)
|
|
988
|
+
return new Derived(root, (value) => value, false) as unknown as DerivedValue
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
class PlaybackValue extends Root {
|
|
992
|
+
constructor(private readonly playback: ReturnType<typeof playbackSource>) {
|
|
993
|
+
super(0)
|
|
994
|
+
const source = playback
|
|
995
|
+
const off = source.subscribe(() => {
|
|
996
|
+
this.changed()
|
|
997
|
+
if (source.read().settled) off()
|
|
998
|
+
})
|
|
999
|
+
if (source.read().settled) off()
|
|
1000
|
+
this.observers.changed(this.running)
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
override raw() {
|
|
1004
|
+
const state = this.playback.read()
|
|
1005
|
+
return (
|
|
1006
|
+
state.position ??
|
|
1007
|
+
(state.completed
|
|
1008
|
+
? 1
|
|
1009
|
+
: state.duration > 0
|
|
1010
|
+
? Math.max(0, Math.min(1, state.time / state.duration))
|
|
1011
|
+
: 0)
|
|
1012
|
+
)
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
override get running() {
|
|
1016
|
+
const state = this.playback.read()
|
|
1017
|
+
return state.ready && !state.settled && !state.paused && state.duration > 0
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
override get paused() {
|
|
1021
|
+
return this.playback.read().paused
|
|
1022
|
+
}
|
|
1023
|
+
override captureTime() {
|
|
1024
|
+
return () => this.playback.read().time
|
|
1025
|
+
}
|
|
1026
|
+
override elapsed() {
|
|
1027
|
+
return this.playback.read().time
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
override plan(binding: Binding): Plan {
|
|
1031
|
+
const state = this.playback.read()
|
|
1032
|
+
if (!state.ready || state.settled || state.duration === 0)
|
|
1033
|
+
return {
|
|
1034
|
+
property: binding.property,
|
|
1035
|
+
keyframes: [],
|
|
1036
|
+
transition: still,
|
|
1037
|
+
track: () => instantTrack(binding.property, binding.read(this.raw())),
|
|
1038
|
+
}
|
|
1039
|
+
// Bypass timeScale: the source duration is already in platform playback milliseconds.
|
|
1040
|
+
const duration = state.duration
|
|
1041
|
+
const at = (ms: number) => binding.read(Math.max(0, Math.min(1, ms / duration)))
|
|
1042
|
+
const track: Track = {
|
|
1043
|
+
property: binding.property,
|
|
1044
|
+
delay: 0,
|
|
1045
|
+
duration,
|
|
1046
|
+
repeat: 0,
|
|
1047
|
+
reverse: false,
|
|
1048
|
+
times: [0, 1],
|
|
1049
|
+
values: [at(0), at(duration)],
|
|
1050
|
+
to: at(duration),
|
|
1051
|
+
end: duration,
|
|
1052
|
+
arrival: duration,
|
|
1053
|
+
valueAt: at,
|
|
1054
|
+
velocityAt: (ms) => (at(ms + 0.5) - at(ms - 0.5)) * 1000,
|
|
1055
|
+
}
|
|
1056
|
+
// Piecewise/color derivations need intermediate samples, not only the two endpoints.
|
|
1057
|
+
const steps = Math.max(1, Math.min(6000, Math.ceil((duration * 60) / 1000)))
|
|
1058
|
+
const times = Array.from({ length: steps + 1 }, (_, i) => i / steps)
|
|
1059
|
+
return {
|
|
1060
|
+
property: binding.property,
|
|
1061
|
+
keyframes: [],
|
|
1062
|
+
transition: still,
|
|
1063
|
+
track: () =>
|
|
1064
|
+
reducedMotion() &&
|
|
1065
|
+
binding.property !== 'opacity' &&
|
|
1066
|
+
!isColorProperty(binding.property)
|
|
1067
|
+
? instantTrack(binding.property, at(duration))
|
|
1068
|
+
: { ...track, times, values: times.map((time) => at(time * duration)) },
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/** Internal compound-binding bridge. One raw snapshot per root keeps sibling mappings coherent. */
|
|
1074
|
+
export function valueSource(source: DerivedValue | DerivedColor, color: boolean) {
|
|
1075
|
+
if (!(source instanceof Derived) || source.color !== color)
|
|
1076
|
+
throw new TypeError(
|
|
1077
|
+
`Gradient component needs a ${color ? 'color' : 'numeric'} Jolt value.`,
|
|
1078
|
+
)
|
|
1079
|
+
const { root, read } = source
|
|
1080
|
+
return {
|
|
1081
|
+
identity: root,
|
|
1082
|
+
read,
|
|
1083
|
+
snapshot() {
|
|
1084
|
+
return {
|
|
1085
|
+
track: root.plan({ property: 'opacity', read: (value) => value } as Binding)
|
|
1086
|
+
.track!(),
|
|
1087
|
+
elapsed: root.elapsed(),
|
|
1088
|
+
time: root.captureTime(),
|
|
1089
|
+
running: root.running,
|
|
1090
|
+
value: root.raw(),
|
|
1091
|
+
}
|
|
1092
|
+
},
|
|
1093
|
+
subscribe(listener: () => void) {
|
|
1094
|
+
root.listeners.add(listener)
|
|
1095
|
+
return () => {
|
|
1096
|
+
root.listeners.delete(listener)
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/** Treat values as opaque identities in declarative option comparisons. */
|
|
1103
|
+
export function isDerived(value: unknown): value is DerivedValue | DerivedColor {
|
|
1104
|
+
return value instanceof Derived
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/** Internal native-adapter bridge. Mappings are sampled at boundaries, never per frame. */
|
|
1108
|
+
export function valuePlayback(source: DerivedValue) {
|
|
1109
|
+
if (!(source instanceof Derived) || source.color)
|
|
1110
|
+
throw new TypeError('A native binding needs a numeric Jolt value.')
|
|
1111
|
+
const { root, read } = source
|
|
1112
|
+
return {
|
|
1113
|
+
snapshot() {
|
|
1114
|
+
return {
|
|
1115
|
+
track: root.plan({ property: 'opacity', read } as Binding).track!(),
|
|
1116
|
+
elapsed: root.elapsed(),
|
|
1117
|
+
paused: root.paused,
|
|
1118
|
+
running: root.running,
|
|
1119
|
+
value: read(root.raw()),
|
|
1120
|
+
}
|
|
1121
|
+
},
|
|
1122
|
+
subscribe(listener: () => void) {
|
|
1123
|
+
root.listeners.add(listener)
|
|
1124
|
+
return () => {
|
|
1125
|
+
root.listeners.delete(listener)
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function release(binding: Binding) {
|
|
1132
|
+
binding.root.bindings.delete(binding)
|
|
1133
|
+
binding.view.off('loaded', binding.sync)
|
|
1134
|
+
const properties = bound.get(binding.view)
|
|
1135
|
+
if (properties?.get(binding.property) === binding) properties.delete(binding.property)
|
|
1136
|
+
binding.cleanup?.()
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/** A regular animation on a bound property takes it over; the value keeps its other bindings. */
|
|
1140
|
+
export function detachBindings(view: View, properties: readonly Property[]) {
|
|
1141
|
+
const map = bound.get(view)
|
|
1142
|
+
if (!map) return
|
|
1143
|
+
for (const property of properties) {
|
|
1144
|
+
const binding = map.get(property)
|
|
1145
|
+
if (binding) release(binding)
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
interface InspectedSource {
|
|
1150
|
+
identity: object
|
|
1151
|
+
kind: string
|
|
1152
|
+
value: number
|
|
1153
|
+
mappings: readonly LiveMapping[]
|
|
1154
|
+
input?: InspectedSource
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/** Read existing binding topology without subscribing or constructing new tracks. */
|
|
1158
|
+
export function inspectViewBindings(view: View) {
|
|
1159
|
+
const describe = (source: Derived, depth = 0): InspectedSource => ({
|
|
1160
|
+
identity: source.root,
|
|
1161
|
+
kind: source.root instanceof SpringRoot ? 'live spring' : 'motion value',
|
|
1162
|
+
value: source.root.raw(),
|
|
1163
|
+
mappings: source.mappings.map((mapping) => ({
|
|
1164
|
+
...mapping,
|
|
1165
|
+
input: [...mapping.input],
|
|
1166
|
+
output: [...mapping.output],
|
|
1167
|
+
})),
|
|
1168
|
+
input:
|
|
1169
|
+
source.root instanceof SpringRoot && depth < 8
|
|
1170
|
+
? describe(source.root.input, depth + 1)
|
|
1171
|
+
: undefined,
|
|
1172
|
+
})
|
|
1173
|
+
return [...(bound.get(view)?.values() ?? [])].map((binding) => ({
|
|
1174
|
+
property: binding.property,
|
|
1175
|
+
source: describe(binding.source ?? binding.root),
|
|
1176
|
+
value: binding.read(binding.root.raw()),
|
|
1177
|
+
}))
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function mapping(
|
|
1181
|
+
input: readonly number[],
|
|
1182
|
+
output: readonly (number | ColorValue)[],
|
|
1183
|
+
options: MapOptions = {},
|
|
1184
|
+
) {
|
|
1185
|
+
if (!Array.isArray(input) || !Array.isArray(output) || input.length < 2)
|
|
1186
|
+
throw new RangeError('to() needs at least two input and output values.')
|
|
1187
|
+
if (input.length !== output.length)
|
|
1188
|
+
throw new RangeError(
|
|
1189
|
+
`to() has ${input.length} inputs but ${output.length} outputs; give one output per input.`,
|
|
1190
|
+
)
|
|
1191
|
+
if (input.some((value) => !Number.isFinite(value)))
|
|
1192
|
+
throw new RangeError('to() inputs must all be finite numbers.')
|
|
1193
|
+
const descending = input[input.length - 1]! < input[0]!
|
|
1194
|
+
if (
|
|
1195
|
+
input.some(
|
|
1196
|
+
(value, i) => i > 0 && (descending ? value > input[i - 1]! : value < input[i - 1]!),
|
|
1197
|
+
)
|
|
1198
|
+
)
|
|
1199
|
+
throw new RangeError(
|
|
1200
|
+
'to() inputs must be monotonic (ascending or descending); ranges cannot change direction.',
|
|
1201
|
+
)
|
|
1202
|
+
// Normalize pairs together, without changing caller-owned arrays or duplicate-stop rules.
|
|
1203
|
+
if (descending) {
|
|
1204
|
+
input = [...input].reverse()
|
|
1205
|
+
output = [...output].reverse()
|
|
1206
|
+
}
|
|
1207
|
+
const color = typeof output[0] !== 'number'
|
|
1208
|
+
const numbers = color ? output.map((value) => parseColor(value)) : (output as number[])
|
|
1209
|
+
if (
|
|
1210
|
+
!color &&
|
|
1211
|
+
numbers.some((value) => typeof value !== 'number' || !Number.isFinite(value))
|
|
1212
|
+
)
|
|
1213
|
+
throw new RangeError('to() outputs must all be finite numbers, or all colors.')
|
|
1214
|
+
const mixers = color
|
|
1215
|
+
? numbers.slice(1).map((value, i) => colorMixer(numbers[i]!, value))
|
|
1216
|
+
: undefined
|
|
1217
|
+
const clamp = color || options.clamp !== false
|
|
1218
|
+
const last = input.length - 1
|
|
1219
|
+
const read = (value: number) => {
|
|
1220
|
+
if (clamp && value <= input[0]!) return numbers[0]!
|
|
1221
|
+
if (clamp && value >= input[last]!) return numbers[last]!
|
|
1222
|
+
let i = 0
|
|
1223
|
+
while (i < last - 1 && value > input[i + 1]!) i++
|
|
1224
|
+
const span = input[i + 1]! - input[i]!
|
|
1225
|
+
const t = span > 0 ? (value - input[i]!) / span : value < input[i]! ? 0 : 1
|
|
1226
|
+
if (mixers) return mixers[i]!(Math.min(1, Math.max(0, t)))
|
|
1227
|
+
return numbers[i]! + (numbers[i + 1]! - numbers[i]!) * t
|
|
1228
|
+
}
|
|
1229
|
+
return { read, color, input, numbers }
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function springOf(name: SpringName): Transition {
|
|
1233
|
+
if (!Object.hasOwn(springs, name)) throw new Error(`Unknown spring "${name}".`)
|
|
1234
|
+
return springs[name]
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/** A number that follows a scroll view's offset. Bind it like a motion value. */
|
|
1238
|
+
export interface ScrollValue extends DerivedValue {
|
|
1239
|
+
/** Follows this scroll view instead, or none with `null`. Stable, so it works as a template ref. */
|
|
1240
|
+
readonly ref: (scrollView: Target | null) => void
|
|
1241
|
+
/** Stops following; bound views stay where they are. */
|
|
1242
|
+
dispose(): void
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* The scroll offset of a ScrollView, in DIPs, as a value that bindings follow.
|
|
1247
|
+
*
|
|
1248
|
+
* ```ts
|
|
1249
|
+
* const offset = scrollValue(scroll)
|
|
1250
|
+
* bind(header, { y: offset.to([0, 120], [0, -60]), opacity: offset.to([0, 80], [1, 0]) })
|
|
1251
|
+
* ```
|
|
1252
|
+
*/
|
|
1253
|
+
export function scrollValue(
|
|
1254
|
+
scrollView?: Target | null,
|
|
1255
|
+
options: { axis?: 'x' | 'y' } = {},
|
|
1256
|
+
): ScrollValue {
|
|
1257
|
+
const axis = options.axis ?? 'y'
|
|
1258
|
+
if (axis !== 'x' && axis !== 'y') throw new TypeError('Scroll axis must be "x" or "y".')
|
|
1259
|
+
const root = new Root(0)
|
|
1260
|
+
let source: ScrollView | undefined
|
|
1261
|
+
const read = () => {
|
|
1262
|
+
if (!source) return
|
|
1263
|
+
const offset = axis === 'x' ? source.horizontalOffset : source.verticalOffset
|
|
1264
|
+
if (Number.isFinite(offset) && offset !== root.raw()) root.set(offset)
|
|
1265
|
+
}
|
|
1266
|
+
const onScroll = (event: EventData) => {
|
|
1267
|
+
const { scrollX, scrollY } = event as ScrollEventData
|
|
1268
|
+
const offset = axis === 'x' ? scrollX : scrollY
|
|
1269
|
+
if (Number.isFinite(offset) && offset !== root.raw()) root.set(offset)
|
|
1270
|
+
}
|
|
1271
|
+
const detach = () => {
|
|
1272
|
+
source?.off(ScrollView.scrollEvent, onScroll)
|
|
1273
|
+
source?.off('loaded', read)
|
|
1274
|
+
source = undefined
|
|
1275
|
+
}
|
|
1276
|
+
const ref = (target: Target | null) => {
|
|
1277
|
+
const next = target === null ? undefined : toViews(target)[0]
|
|
1278
|
+
if (next === source) return
|
|
1279
|
+
if (next !== undefined && !(next instanceof ScrollView))
|
|
1280
|
+
throw new TypeError('scrollValue() follows a NativeScript ScrollView.')
|
|
1281
|
+
detach()
|
|
1282
|
+
source = next
|
|
1283
|
+
source?.on(ScrollView.scrollEvent, onScroll)
|
|
1284
|
+
source?.on('loaded', read)
|
|
1285
|
+
read()
|
|
1286
|
+
}
|
|
1287
|
+
if (scrollView) ref(scrollView)
|
|
1288
|
+
return Object.assign(root as unknown as DerivedValue, { ref, dispose: detach })
|
|
1289
|
+
}
|