@rafaelaugusto/jolt 0.1.0-beta.1 → 0.1.0-beta.11
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 +385 -18
- package/dist/animate.d.ts +19 -3
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +186 -14
- package/dist/animate.js.map +1 -1
- package/dist/color.d.ts +11 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +73 -0
- package/dist/color.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +73 -0
- package/dist/config.js.map +1 -0
- package/dist/decay.d.ts +11 -0
- package/dist/decay.d.ts.map +1 -0
- package/dist/decay.js +22 -0
- package/dist/decay.js.map +1 -0
- package/dist/decoration.d.ts +18 -0
- package/dist/decoration.d.ts.map +1 -0
- package/dist/decoration.js +155 -0
- package/dist/decoration.js.map +1 -0
- package/dist/drag-input.d.ts +15 -0
- package/dist/drag-input.d.ts.map +1 -0
- package/dist/drag-input.js +66 -0
- package/dist/drag-input.js.map +1 -0
- package/dist/drag-physics.d.ts +20 -0
- package/dist/drag-physics.d.ts.map +1 -0
- package/dist/drag-physics.js +75 -0
- package/dist/drag-physics.js.map +1 -0
- package/dist/drag.d.ts +58 -0
- package/dist/drag.d.ts.map +1 -0
- package/dist/drag.js +324 -0
- package/dist/drag.js.map +1 -0
- package/dist/driver/android-color.d.ts +20 -0
- package/dist/driver/android-color.d.ts.map +1 -0
- package/dist/driver/android-color.js +136 -0
- package/dist/driver/android-color.js.map +1 -0
- package/dist/driver/android-decoration.d.ts +22 -0
- package/dist/driver/android-decoration.d.ts.map +1 -0
- package/dist/driver/android-decoration.js +139 -0
- package/dist/driver/android-decoration.js.map +1 -0
- 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 +89 -0
- package/dist/driver/android-gradient.js.map +1 -0
- package/dist/driver/android-playback.d.ts +13 -0
- package/dist/driver/android-playback.d.ts.map +1 -0
- package/dist/driver/android-playback.js +141 -0
- package/dist/driver/android-playback.js.map +1 -0
- package/dist/driver/android.d.ts +1 -1
- package/dist/driver/android.d.ts.map +1 -1
- package/dist/driver/android.js +165 -13
- package/dist/driver/android.js.map +1 -1
- package/dist/driver/index.d.ts +25 -2
- package/dist/driver/index.d.ts.map +1 -1
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/ios-color.d.ts +16 -0
- package/dist/driver/ios-color.d.ts.map +1 -0
- package/dist/driver/ios-color.js +392 -0
- package/dist/driver/ios-color.js.map +1 -0
- package/dist/driver/ios-decoration.d.ts +17 -0
- package/dist/driver/ios-decoration.d.ts.map +1 -0
- package/dist/driver/ios-decoration.js +271 -0
- package/dist/driver/ios-decoration.js.map +1 -0
- 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 +109 -0
- package/dist/driver/ios-gradient.js.map +1 -0
- package/dist/driver/ios-layout.d.ts +9 -0
- package/dist/driver/ios-layout.d.ts.map +1 -0
- package/dist/driver/ios-layout.js +52 -0
- package/dist/driver/ios-layout.js.map +1 -0
- package/dist/driver/ios-playback.d.ts +10 -0
- package/dist/driver/ios-playback.d.ts.map +1 -0
- package/dist/driver/ios-playback.js +47 -0
- package/dist/driver/ios-playback.js.map +1 -0
- package/dist/driver/ios-shadow.d.ts +6 -0
- package/dist/driver/ios-shadow.d.ts.map +1 -0
- package/dist/driver/ios-shadow.js +52 -0
- package/dist/driver/ios-shadow.js.map +1 -0
- package/dist/driver/ios.d.ts.map +1 -1
- package/dist/driver/ios.js +163 -28
- package/dist/driver/ios.js.map +1 -1
- package/dist/engine.d.ts +75 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +293 -23
- package/dist/engine.js.map +1 -1
- package/dist/gradient.d.ts +90 -0
- package/dist/gradient.d.ts.map +1 -0
- package/dist/gradient.js +247 -0
- package/dist/gradient.js.map +1 -0
- package/dist/haptics.d.ts +21 -0
- package/dist/haptics.d.ts.map +1 -0
- package/dist/haptics.js +120 -0
- package/dist/haptics.js.map +1 -0
- package/dist/image-effect-native.d.ts +26 -0
- package/dist/image-effect-native.d.ts.map +1 -0
- package/dist/image-effect-native.js +73 -0
- package/dist/image-effect-native.js.map +1 -0
- package/dist/image-effect.d.ts +50 -0
- package/dist/image-effect.d.ts.map +1 -0
- package/dist/image-effect.js +290 -0
- package/dist/image-effect.js.map +1 -0
- package/dist/image-owner.d.ts +3 -0
- package/dist/image-owner.d.ts.map +1 -0
- package/dist/image-owner.js +11 -0
- package/dist/image-owner.js.map +1 -0
- package/dist/index.d.ts +47 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/interaction-surface.d.ts +12 -0
- package/dist/interaction-surface.d.ts.map +1 -0
- package/dist/interaction-surface.js +36 -0
- package/dist/interaction-surface.js.map +1 -0
- package/dist/items.d.ts +20 -0
- package/dist/items.d.ts.map +1 -0
- package/dist/items.js +109 -0
- package/dist/items.js.map +1 -0
- package/dist/keyboard.d.ts +41 -0
- package/dist/keyboard.d.ts.map +1 -0
- package/dist/keyboard.js +256 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/label.d.ts +7 -0
- package/dist/label.d.ts.map +1 -0
- package/dist/label.js +60 -0
- package/dist/label.js.map +1 -0
- package/dist/layout.d.ts +13 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +348 -0
- package/dist/layout.js.map +1 -0
- package/dist/liquid-grid.d.ts +3 -0
- package/dist/liquid-grid.d.ts.map +1 -0
- package/dist/liquid-grid.js +24 -0
- package/dist/liquid-grid.js.map +1 -0
- package/dist/liquid.d.ts +18 -0
- package/dist/liquid.d.ts.map +1 -0
- package/dist/liquid.js +234 -0
- package/dist/liquid.js.map +1 -0
- package/dist/loader.d.ts +19 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +173 -0
- package/dist/loader.js.map +1 -0
- package/dist/motion-scope.d.ts +39 -0
- package/dist/motion-scope.d.ts.map +1 -0
- package/dist/motion-scope.js +238 -0
- package/dist/motion-scope.js.map +1 -0
- package/dist/motion-states.d.ts +43 -0
- package/dist/motion-states.d.ts.map +1 -0
- package/dist/motion-states.js +220 -0
- package/dist/motion-states.js.map +1 -0
- package/dist/particles.d.ts +39 -0
- package/dist/particles.d.ts.map +1 -0
- package/dist/particles.js +265 -0
- package/dist/particles.js.map +1 -0
- package/dist/path.d.ts +12 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +90 -0
- package/dist/path.js.map +1 -0
- package/dist/playback-group.d.ts +6 -0
- package/dist/playback-group.d.ts.map +1 -0
- package/dist/playback-group.js +141 -0
- package/dist/playback-group.js.map +1 -0
- package/dist/playback.d.ts +25 -0
- package/dist/playback.d.ts.map +1 -0
- package/dist/playback.js +35 -0
- package/dist/playback.js.map +1 -0
- package/dist/press.d.ts.map +1 -1
- package/dist/press.js +23 -7
- package/dist/press.js.map +1 -1
- package/dist/properties.d.ts +6 -3
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +26 -2
- package/dist/properties.js.map +1 -1
- package/dist/reveal.d.ts +25 -0
- package/dist/reveal.d.ts.map +1 -0
- package/dist/reveal.js +294 -0
- package/dist/reveal.js.map +1 -0
- package/dist/roll-diff.d.ts +22 -0
- package/dist/roll-diff.d.ts.map +1 -0
- package/dist/roll-diff.js +36 -0
- package/dist/roll-diff.js.map +1 -0
- package/dist/roll.d.ts +29 -0
- package/dist/roll.d.ts.map +1 -0
- package/dist/roll.js +442 -0
- package/dist/roll.js.map +1 -0
- package/dist/scroll-linked.d.ts +10 -0
- package/dist/scroll-linked.d.ts.map +1 -0
- package/dist/scroll-linked.js +58 -0
- package/dist/scroll-linked.js.map +1 -0
- package/dist/sequence.d.ts +10 -5
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +208 -14
- package/dist/sequence.js.map +1 -1
- package/dist/shimmer.d.ts +12 -0
- package/dist/shimmer.d.ts.map +1 -0
- package/dist/shimmer.js +161 -0
- package/dist/shimmer.js.map +1 -0
- package/dist/svg-path.d.ts +14 -0
- package/dist/svg-path.d.ts.map +1 -0
- package/dist/svg-path.js +281 -0
- package/dist/svg-path.js.map +1 -0
- package/dist/symbol-effect.d.ts +23 -0
- package/dist/symbol-effect.d.ts.map +1 -0
- package/dist/symbol-effect.js +187 -0
- package/dist/symbol-effect.js.map +1 -0
- package/dist/text-layout.d.ts +30 -0
- package/dist/text-layout.d.ts.map +1 -0
- package/dist/text-layout.js +185 -0
- package/dist/text-layout.js.map +1 -0
- package/dist/text.d.ts +17 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +263 -0
- package/dist/text.js.map +1 -0
- package/dist/track.d.ts +3 -1
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +266 -13
- package/dist/track.js.map +1 -1
- package/dist/transition.d.ts +32 -1
- package/dist/transition.d.ts.map +1 -1
- package/dist/transition.js +58 -6
- package/dist/transition.js.map +1 -1
- package/dist/types.d.ts +48 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vue/index.d.ts +95 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +163 -0
- package/dist/vue/index.js.map +1 -0
- package/dist/zoom-native.d.ts +30 -0
- package/dist/zoom-native.d.ts.map +1 -0
- package/dist/zoom-native.js +56 -0
- package/dist/zoom-native.js.map +1 -0
- package/dist/zoomable.d.ts +48 -0
- package/dist/zoomable.d.ts.map +1 -0
- package/dist/zoomable.js +249 -0
- package/dist/zoomable.js.map +1 -0
- package/package.json +29 -5
- package/platforms/android/include.gradle +7 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ColorTarget.java +102 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/DecorationTarget.java +154 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/GradientLayer.java +195 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ImageEffect.java +288 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFollower.java +91 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardObserver.java +97 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/NumberRoll.java +190 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/PhotoZoom.java +250 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/TextReveal.java +180 -0
- package/platforms/android/proguard-rules.pro +21 -0
- package/platforms/ios/build.xcconfig +1 -0
- package/platforms/ios/src/JoltImageEffect.h +25 -0
- package/platforms/ios/src/JoltImageEffect.mm +242 -0
- package/platforms/ios/src/JoltInteractiveAnimation.h +18 -0
- package/platforms/ios/src/JoltInteractiveAnimation.m +123 -0
- package/platforms/ios/src/JoltPhotoZoom.h +23 -0
- package/platforms/ios/src/JoltPhotoZoom.m +229 -0
- package/platforms/ios/src/module.modulemap +6 -0
- package/src/android-plugin.d.ts +184 -0
- package/src/animate.ts +249 -36
- package/src/color.ts +101 -0
- package/src/config.ts +84 -0
- package/src/decay.ts +45 -0
- package/src/decoration.ts +180 -0
- package/src/drag-input.ts +74 -0
- package/src/drag-physics.ts +110 -0
- package/src/drag.ts +395 -0
- package/src/driver/android-color.ts +170 -0
- package/src/driver/android-decoration.ts +182 -0
- package/src/driver/android-gradient.ts +109 -0
- package/src/driver/android-playback.ts +157 -0
- package/src/driver/android.ts +208 -20
- package/src/driver/index.ts +37 -2
- package/src/driver/ios-color.ts +441 -0
- package/src/driver/ios-decoration.ts +332 -0
- package/src/driver/ios-gradient.ts +114 -0
- package/src/driver/ios-layout.ts +63 -0
- package/src/driver/ios-playback.ts +51 -0
- package/src/driver/ios-shadow.ts +50 -0
- package/src/driver/ios.ts +193 -33
- package/src/engine.ts +321 -27
- package/src/gradient.ts +372 -0
- package/src/haptics.ts +148 -0
- package/src/image-effect-native.ts +129 -0
- package/src/image-effect.ts +336 -0
- package/src/image-owner.ts +12 -0
- package/src/index.ts +75 -4
- package/src/interaction-surface.ts +41 -0
- package/src/ios-plugin.d.ts +81 -0
- package/src/items.ts +140 -0
- package/src/keyboard.ts +291 -0
- package/src/label.ts +76 -0
- package/src/layout.ts +408 -0
- package/src/liquid-grid.ts +28 -0
- package/src/liquid.ts +287 -0
- package/src/loader.ts +202 -0
- package/src/motion-scope.ts +279 -0
- package/src/motion-states.ts +269 -0
- package/src/particles.ts +323 -0
- package/src/path.ts +124 -0
- package/src/playback-group.ts +143 -0
- package/src/playback.ts +62 -0
- package/src/press.ts +26 -10
- package/src/properties.ts +32 -6
- package/src/reveal.ts +374 -0
- package/src/roll-diff.ts +57 -0
- package/src/roll.ts +515 -0
- package/src/scroll-linked.ts +73 -0
- package/src/sequence.ts +204 -14
- package/src/shimmer.ts +180 -0
- package/src/svg-path.ts +355 -0
- package/src/symbol-effect.ts +226 -0
- package/src/text-layout.ts +228 -0
- package/src/text.ts +279 -0
- package/src/track.ts +347 -12
- package/src/transition.ts +93 -6
- package/src/types.ts +49 -1
- package/src/vue/index.ts +231 -0
- package/src/zoom-native.ts +124 -0
- package/src/zoomable.ts +330 -0
package/README.md
CHANGED
|
@@ -35,10 +35,36 @@ Jolt computes the motion once, in JavaScript, when the animation starts, and han
|
|
|
35
35
|
|
|
36
36
|
- [25 presets](#presets) to enter, exit and draw attention: `fadeInUp`, `popIn`, `shake`, `heartbeat`...
|
|
37
37
|
- [Springs](#springs) described by feel (`bouncy`), by `bounce` and `duration`, or by stiffness, damping and mass.
|
|
38
|
+
- [Corners, borders and shadows](#corners-borders-and-shadows) with native playback and selective stopping.
|
|
39
|
+
- [Motion paths](#motion-along-a-path) with optional tangent orientation on iOS and Android.
|
|
40
|
+
- [Colors](#colors) for backgrounds, borders and text, interpolated in OKLab on iOS and Android.
|
|
41
|
+
- [Gradients](#gradients): native linear, radial and conic gradients with animated colors, angle, center and radius.
|
|
42
|
+
- [Motion scopes and states](https://jolt.rafaelaugusto.dev/guide/motion-controllers) for component cleanup and coordinated visual states.
|
|
43
|
+
- [Animated touch targets](https://jolt.rafaelaugusto.dev/guide/interaction-surface) with an opt-in native interaction surface.
|
|
44
|
+
- [Layout changes](https://jolt.rafaelaugusto.dev/guide/layout): insertion, removal, reorder and resize of container children.
|
|
45
|
+
- [Drag](https://jolt.rafaelaugusto.dev/guide/drag) with bounds, snap points, rubberband and native release physics.
|
|
46
|
+
- [Decay](#decay) from a release velocity, with optional snap points.
|
|
38
47
|
- [Keyframes and tweens](#tweens) with an easing per segment and exact keyframe times.
|
|
39
48
|
- [Stagger](#several-views) across several views, from the first, the last or the center.
|
|
40
|
-
- [
|
|
49
|
+
- [List rows](#list-rows) that play an entrance as they scroll into view, every time or once per item.
|
|
50
|
+
- [Sequences](#sequences) with overlapping arrivals, shared starts and absolute timeline positions.
|
|
41
51
|
- [Press effects](#press) that follow the finger and let go when a scroll takes over.
|
|
52
|
+
- [Vue directives and a transition component](#vue) from the optional `@rafaelaugusto/jolt/vue` entry.
|
|
53
|
+
- [Text entrances](#text) by letter or word on iOS and Android, preserving the Label’s layout and accessibility.
|
|
54
|
+
- [Rolling numbers](#rolling-numbers) that slide only the changed digits in and out, on iOS and Android.
|
|
55
|
+
- [SF Symbol effects](#sf-symbols) and animated icon replacement on iOS 17+.
|
|
56
|
+
- [Shimmer](#shimmer) for loading placeholders on iOS.
|
|
57
|
+
- [Liquid images](#liquid-images) with native texture deformation on iOS.
|
|
58
|
+
- [Image effects](https://jolt.rafaelaugusto.dev/guide/image-effects): ripple, touch distortion and dissolve with native Metal/AGSL rendering and explicit fallback.
|
|
59
|
+
- [Photo zoom and pan](https://jolt.rafaelaugusto.dev/guide/photo-zoom): native pinch, double tap, bounds and vertical dismissal intents.
|
|
60
|
+
- [Loaders](#loaders) with dots, bars and ring styles on iOS.
|
|
61
|
+
- [Circular reveal](#circular-reveal) to show or hide content from a point on iOS.
|
|
62
|
+
- [Haptics](#haptics) at arrival or at marked points in an animation on iOS.
|
|
63
|
+
- [Particles and confetti](#particles-and-confetti) on iOS.
|
|
64
|
+
- [Scroll-linked animations](#scroll-linked-animations) for parallax and collapsing headers.
|
|
65
|
+
- [Keyboard following](#follow-the-keyboard) for composers that rise with the keyboard, at its speed and curve.
|
|
66
|
+
- [Playback controls](#pause-play-and-scrub) to pause, continue and scrub a finite timeline.
|
|
67
|
+
- [Time scale and reduced motion](#time-scale-and-reduced-motion): slow everything down while tuning, and follow the platform's reduced-motion setting by default.
|
|
42
68
|
- [Interruptions](#interrupting-and-stopping) that pick up the value and velocity on screen, with no jump.
|
|
43
69
|
- [Native playback](#how-it-works): Core Animation on iOS, `ObjectAnimator` on Android, nothing in JavaScript while it plays.
|
|
44
70
|
|
|
@@ -50,6 +76,8 @@ npm install @rafaelaugusto/jolt@next
|
|
|
50
76
|
|
|
51
77
|
It's in beta for now, published under the `next` tag. Requires `@nativescript/core` 8.9 or later. It doesn't depend on a framework: it takes a view, or an element that holds one.
|
|
52
78
|
|
|
79
|
+
On iOS 15+, Jolt requests up to 120 Hz on ProMotion displays. Set `CADisableMinimumFrameDurationOnPhone` to `true` in the app's `App_Resources/iOS/Info.plist` and rebuild to enable higher rates on iPhone. See the [iOS setup](https://jolt.rafaelaugusto.dev/guide/install#promotion-on-ios); the system still chooses the actual refresh rate.
|
|
80
|
+
|
|
53
81
|
## Animating
|
|
54
82
|
|
|
55
83
|
`animate(target, keyframes, transition?)` takes a view and where to take it:
|
|
@@ -58,14 +86,17 @@ It's in beta for now, published under the `next` tag. Requires `@nativescript/co
|
|
|
58
86
|
animate(card, { opacity: 0.5, y: -20 })
|
|
59
87
|
```
|
|
60
88
|
|
|
61
|
-
| Property
|
|
62
|
-
|
|
|
63
|
-
| `opacity`
|
|
64
|
-
| `x`, `y`
|
|
65
|
-
| `scale`
|
|
66
|
-
| `scaleX`, `scaleY`
|
|
67
|
-
| `rotate`
|
|
68
|
-
| `rotateX`, `rotateY`
|
|
89
|
+
| Property | Unit |
|
|
90
|
+
| ----------------------------------------- | ---------------------------------------- |
|
|
91
|
+
| `opacity` | |
|
|
92
|
+
| `x`, `y` | DIPs, same as `translateX`, `translateY` |
|
|
93
|
+
| `scale` | sets `scaleX` and `scaleY` together |
|
|
94
|
+
| `scaleX`, `scaleY` | |
|
|
95
|
+
| `rotate` | degrees |
|
|
96
|
+
| `rotateX`, `rotateY` | degrees, with perspective |
|
|
97
|
+
| `backgroundColor`, `borderColor`, `color` | NativeScript color string or `Color` |
|
|
98
|
+
|
|
99
|
+
3D rotations and flip presets use the view's `perspective` on both platforms (NativeScript defaults to 1000 DIPs). Set `card.perspective = 600` for stronger depth. Android applies the display density when configuring its camera distance; it keeps the perspective when a rotation ends or stops at an angle.
|
|
69
100
|
|
|
70
101
|
A single value animates from wherever the view is. An array is a list of keyframes, and `null` stands for the current value:
|
|
71
102
|
|
|
@@ -98,9 +129,82 @@ const open = () => animate(card.value, 'popIn')
|
|
|
98
129
|
|
|
99
130
|
A view that isn't loaded yet takes its first absolute keyframe right away and plays once it's on screen. Calling `animate(title, 'fadeInUp')` in `onMounted` doesn't flash the title before it fades in. Relative animations leave the view's values alone until it loads.
|
|
100
131
|
|
|
132
|
+
## Vue
|
|
133
|
+
|
|
134
|
+
```vue
|
|
135
|
+
<script setup lang="ts">
|
|
136
|
+
import { JoltTransition, vAnimate, vPress } from '@rafaelaugusto/jolt/vue'
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<template>
|
|
140
|
+
<Label v-animate="'fadeInUp'" text="Hello" />
|
|
141
|
+
<GridLayout v-press="{ scale: 0.95 }" />
|
|
142
|
+
<JoltTransition enter="popIn" leave="fadeOut">
|
|
143
|
+
<Label v-if="saved" text="Saved" />
|
|
144
|
+
</JoltTransition>
|
|
145
|
+
</template>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`@rafaelaugusto/jolt/vue` is a separate entry point for NativeScript-Vue 3, so the core stays framework free. `v-animate` plays when the element mounts and again when its value changes, `v-press` is `press()` for templates, and `JoltTransition` wraps Vue's `BaseTransition` so a view can animate out before `v-if` removes it. Register everything with `app.use(Jolt)` or import what a component needs. See the [Vue guide](https://jolt.rafaelaugusto.dev/guide/vue).
|
|
149
|
+
|
|
150
|
+
## Motion along a path
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
animate(
|
|
154
|
+
plane,
|
|
155
|
+
{ path: 'M0 0 C80 -120 200 -120 280 0', orient: true },
|
|
156
|
+
{ duration: 1200, ease: 'linear' },
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
SVG coordinates are translations in DIPs. `orient: true` aligns artwork facing +X with the forward tangent. One shared progression drives native `x`, `y` and optional `rotate` tracks, with arc-length sampling, tweens, springs, delay, repeats, stagger, relative offsets and playback controls. Paths own those coordinates; use a `path` transition override to time them together. Parsing and sampling happen once; no JavaScript runs per frame. See the [path guide](https://jolt.rafaelaugusto.dev/guide/paths) for SVG commands, orientation and geometry limits.
|
|
161
|
+
|
|
162
|
+
## Corners, borders and shadows
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
animate(card, { borderRadius: 40, borderWidth: 6 }, { duration: 800 })
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Uniform circular borders work on iOS and Android. On iOS, configure one outer `boxShadow` and animate `shadowOpacity`, `shadowRadius` (half Core's CSS blur radius), `shadowOffsetX` and `shadowOffsetY`. On Android, animate `elevation` and `translationZ` for native shadows. Dimensions use DIPs; interruption, springs, repeats and selective stop work as usual. Border width animates the stroke while content uses the final layout. Clip paths, nonuniform geometry and Android radius changes with CSS shadows are rejected. Rebuild Android after upgrading. See the [guide and platform limits](https://jolt.rafaelaugusto.dev/guide/decorations).
|
|
169
|
+
|
|
170
|
+
## Colors
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
animate(
|
|
174
|
+
card,
|
|
175
|
+
{
|
|
176
|
+
backgroundColor: '#263b27',
|
|
177
|
+
borderColor: ['#b6ee64', '#e5a1ff'],
|
|
178
|
+
color: '#f4caff',
|
|
179
|
+
},
|
|
180
|
+
{ duration: 800 },
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Color strings and NativeScript `Color` objects work as targets or keyframes; `null` starts from the visible color. Colors interpolate in premultiplied OKLab and stay absolute with `relative: true`. Interruptions, repeats and `stop(card, ['backgroundColor'])` use the same API as transforms. Colors default to a 300 ms tween.
|
|
185
|
+
|
|
186
|
+
`borderColor` requires the same starting color on every edge; widths and corner radii may differ. Text color supports Label and Button on iOS, and TextBase on Android. Explicit formatted-span colors stay intact. On iOS, text uses a native alpha-mask layer preserving UILabel's layout; emoji, attachments, text shadows and masks above four million pixels keep the final model color and resolve `false`. Edits, restyling and unloading cancel text playback and restore the current text. Gradients and images themselves are not interpolated. iOS background-color playback on an image background is skipped to preserve Core’s rasterized image; the final model color is applied. See the [color guide](https://jolt.rafaelaugusto.dev/guide/colors).
|
|
187
|
+
|
|
188
|
+
Android includes a Java animation target; rebuild the native app after installing or upgrading Jolt.
|
|
189
|
+
|
|
190
|
+
## Gradients
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { gradient } from '@rafaelaugusto/jolt'
|
|
194
|
+
|
|
195
|
+
const glow = gradient(card, {
|
|
196
|
+
type: 'radial',
|
|
197
|
+
colors: ['#A78BFA', '#0F172A'],
|
|
198
|
+
radius: 0.45,
|
|
199
|
+
})
|
|
200
|
+
glow.animate({ center: { x: 0.7, y: 0.7 }, radius: 0.9 }, { duration: 1400 })
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Native linear, radial and conic gradients behind a layout's children, which NativeScript's CSS doesn't have beyond a static `linear-gradient`. Colors, stops, angle, center and radius animate with springs, tweens and repeats: a `CAGradientLayer` with Core Animation keyframes on iOS, a native drawable and animator on Android. Rebuild Android after upgrading. See the [guide](https://jolt.rafaelaugusto.dev/guide/gradients).
|
|
204
|
+
|
|
101
205
|
## Transitions
|
|
102
206
|
|
|
103
|
-
The third argument says how to get there. Without one, transforms move with a spring and
|
|
207
|
+
The third argument says how to get there. Without one, transforms move with a spring and opacity and colors ease over 300ms. Keyframes tween.
|
|
104
208
|
|
|
105
209
|
### Springs
|
|
106
210
|
|
|
@@ -118,6 +222,8 @@ animate(card, { y: 0 }, { stiffness: 300, damping: 20, mass: 1 })
|
|
|
118
222
|
|
|
119
223
|
`bounce` goes from -1 to 1: 0 settles without overshooting, higher values bounce more, negative values settle more slowly. `duration` is how long the spring feels, the same model as SwiftUI. Or describe it with `stiffness`, `damping` and `mass`. A spring starts with the velocity of the animation it interrupts, unless you pass `velocity` (units per second).
|
|
120
224
|
|
|
225
|
+
Coming from React Native, `origamiSpring({ friction: 7, tension: 40 })` gives the spring `Animated.spring` plays for `friction` and `tension`. See [From React Native Animated](https://jolt.rafaelaugusto.dev/guide/react-native) for the rest of the mapping.
|
|
226
|
+
|
|
121
227
|
### Tweens
|
|
122
228
|
|
|
123
229
|
```ts
|
|
@@ -131,6 +237,18 @@ animate(
|
|
|
131
237
|
|
|
132
238
|
Tweens take 300ms and `easeOut` unless told otherwise. `ease` takes `linear`, `ease`, `easeIn`, `easeOut`, `easeInOut`, `circIn`, `circOut`, `circInOut`, `backIn`, `backOut`, `backInOut`, `anticipate`, a cubic bezier (`[0.2, 0, 0, 1]`) or any function from 0..1 to a value. With keyframes, pass one per segment. `times` places each keyframe between 0 and 1.
|
|
133
239
|
|
|
240
|
+
With `easeTrack: true`, one `ease` runs over the whole track instead, and the value moves in straight lines between keyframes, the way React Native's `interpolate` places its output range on an eased value:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
animate(
|
|
244
|
+
spark,
|
|
245
|
+
{ y: [0, -30, -12, 0] },
|
|
246
|
+
{ duration: 900, ease: 'easeOut', easeTrack: true, times: [0, 0.4, 0.7, 1] },
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
An easing that overshoots, like `backOut`, extends the first or last segment, as `interpolate` does with `extrapolate: 'extend'`. `easeTrack` takes a single `ease` and applies to tweens only.
|
|
251
|
+
|
|
134
252
|
Custom easing functions run once, when the animation starts, like springs do. They don't cost anything per frame.
|
|
135
253
|
|
|
136
254
|
### Per property
|
|
@@ -157,6 +275,31 @@ animate(
|
|
|
157
275
|
|
|
158
276
|
`delay` is in milliseconds, and the view takes the first keyframe during it. `repeat` is how many more times to play, `Infinity` until stopped. `repeatType: 'reverse'` plays every other repeat backwards. Repeats play natively too.
|
|
159
277
|
|
|
278
|
+
`repeatDelay` holds the end value for that many milliseconds before each repeat. The first play doesn't wait, and neither does the end of the last one. With `'reverse'` it waits at both ends:
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
animate(
|
|
282
|
+
z,
|
|
283
|
+
{ y: [0, -40], opacity: [0, 1] },
|
|
284
|
+
{ duration: 1200, repeat: Infinity, repeatDelay: 1800 },
|
|
285
|
+
)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The wait is part of the native keyframes, so it costs no JavaScript while it plays. Stopping during the wait leaves the view at the held value.
|
|
289
|
+
|
|
290
|
+
## Decay
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { decay } from '@rafaelaugusto/jolt'
|
|
294
|
+
|
|
295
|
+
decay(card, { x: 600 }) // DIPs per second
|
|
296
|
+
decay(card, { x: 600 }, { friction: 0.998, snap: [0, 320] })
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Starts from the current translation and slows down exponentially. `friction` multiplies velocity per millisecond; smaller values stop sooner. `snap` projects the landing and springs directly to the closest stop, preserving the supplied velocity. Use `{ snap: { x: [0, 320], y: [-200, 0] } }` for independent axes.
|
|
300
|
+
|
|
301
|
+
Shares interruption, `stop()` and awaitable completion with `animate()`. Both platforms use native keyframes with no JavaScript per frame. See the [decay guide](https://jolt.rafaelaugusto.dev/guide/decay).
|
|
302
|
+
|
|
160
303
|
## Presets
|
|
161
304
|
|
|
162
305
|
```ts
|
|
@@ -217,6 +360,15 @@ animate(tiles, 'popIn', { stagger: { each: 30, from: 'center' } })
|
|
|
217
360
|
|
|
218
361
|
`from` is `'first'` (the default), `'last'`, `'center'` or an index.
|
|
219
362
|
|
|
363
|
+
## List rows
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
const unlink = animateItems(listView, 'fadeInUp')
|
|
367
|
+
animateItems(listView, 'fadeInUp', { once: true })
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Each row of a `ListView` plays the entrance as the list hands it out. Rows are recycled while you scroll, so a row reused for another item starts the entrance again, or settles at the end values when that item was already shown and `once` is set. Items are tracked by object identity, or by index for primitives. The return value removes the effect and settles every row; unloading the list does the same. See the [list guide](https://jolt.rafaelaugusto.dev/guide/lists) for CollectionView and sectioned lists.
|
|
371
|
+
|
|
220
372
|
## Sequences
|
|
221
373
|
|
|
222
374
|
```ts
|
|
@@ -231,6 +383,19 @@ Each step takes the same arguments as `animate`, or is a function that starts an
|
|
|
231
383
|
|
|
232
384
|
Views in later steps take their first absolute keyframe when the sequence starts, so the title above stays hidden until its turn instead of showing up and disappearing. Relative steps and `null` keyframes wait until their step starts to read the current value.
|
|
233
385
|
|
|
386
|
+
Use `at` to change the timing:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
sequence([
|
|
390
|
+
[logo, 'popIn'],
|
|
391
|
+
[title, 'fadeInUp', { at: '-150' }],
|
|
392
|
+
[buttons, 'fadeIn', { at: '<' }],
|
|
393
|
+
[footer, 'fadeIn', { at: 900 }],
|
|
394
|
+
])
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
A signed string offsets the previous arrival (`'-150'` before, `'+100'` after), `'<'` shares its activation, and a number selects absolute milliseconds. Each step's own delay still applies. Negative offsets use loaded tracks' spring/stagger arrival forecasts; absolute steps are independent. `stop()` cancels future launches and stops every started step. Position timers respect Android animator duration scale. JavaScript runs at step boundaries, so blocked JS can delay a launch. See [timelines](https://jolt.rafaelaugusto.dev/guide/sequences) for callback forecasts, interruption and loading behavior.
|
|
398
|
+
|
|
234
399
|
## Press
|
|
235
400
|
|
|
236
401
|
```ts
|
|
@@ -241,6 +406,166 @@ press(tile, { scale: 0.9, rotate: -4 }, 'bouncy')
|
|
|
241
406
|
|
|
242
407
|
Animates to the given values while a finger is down, and back to where the view was once it lifts, or once the touch is cancelled by a scroll taking over. Views with `isEnabled` set to `false` don't react. It returns a function that removes the effect.
|
|
243
408
|
|
|
409
|
+
## SF Symbols
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
import { symbolEffect } from '@rafaelaugusto/jolt'
|
|
413
|
+
|
|
414
|
+
symbolEffect(bell, 'bounce')
|
|
415
|
+
symbolEffect(favorite, { replace: 'sys://heart.fill' })
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Takes a NativeScript `Image` with a `sys://` source, or a ref holding one. Supports `bounce`, `pulse`, `scale` and `variableColor`, with native playback, completion and cancellation. Replacement keeps the Image's source in sync. Scale holds until stopped; bounce, pulse and variable color can repeat with `{ repeat: true }`.
|
|
419
|
+
|
|
420
|
+
Effects require iOS 17+. Earlier iOS versions and Reduce Motion skip decorative effects and replace icons immediately. Android calls resolve with `false` without changing the image. See [SF Symbol effects](https://jolt.rafaelaugusto.dev/guide/symbol-effects) for options and lifecycle behavior.
|
|
421
|
+
|
|
422
|
+
## Shimmer
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
import { shimmer } from '@rafaelaugusto/jolt'
|
|
426
|
+
|
|
427
|
+
const stopShimmer = shimmer(placeholder, { duration: 1500 })
|
|
428
|
+
stopShimmer() // when the content is ready
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
A native gradient mask sweeps across the placeholder and its children. It follows layout changes, preserves existing masks and releases native resources while the view is unloaded, resuming on load. Reduce Motion keeps the placeholder static. iOS only for now; Android calls return a no-op cleanup function. See the [shimmer guide](https://jolt.rafaelaugusto.dev/guide/shimmer).
|
|
432
|
+
|
|
433
|
+
## Liquid images
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { liquid } from '@rafaelaugusto/jolt'
|
|
437
|
+
|
|
438
|
+
const flow = liquid(orb, { intensity: 0.9, duration: 7200 })
|
|
439
|
+
flow.pause()
|
|
440
|
+
flow.resume()
|
|
441
|
+
flow.stop()
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Parts of the image move relative to each other through a native repeating texture warp. Transparency and stretch are preserved; pause freezes the current shape and stop restores the original image. Reduce Motion keeps the image static. iOS only; Android returns an inert handle. See the [liquid image guide](https://jolt.rafaelaugusto.dev/guide/liquid).
|
|
445
|
+
|
|
446
|
+
## Loaders
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
import { loader } from '@rafaelaugusto/jolt'
|
|
450
|
+
|
|
451
|
+
const stopLoading = loader(slot, { style: 'dots', color: '#71717a', size: 48 })
|
|
452
|
+
stopLoading() // when the content is ready
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Choose `dots`, `bars` or `ring`. Each indicator uses one native animation replicated across a layer, fits inside its target and stays centered as the target resizes. Reduce Motion shows a static indicator. Unloading releases native resources; loading resumes until explicitly stopped. iOS only for now. See the [loaders guide](https://jolt.rafaelaugusto.dev/guide/loaders).
|
|
456
|
+
|
|
457
|
+
## Circular reveal
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
import { reveal, conceal } from '@rafaelaugusto/jolt'
|
|
461
|
+
|
|
462
|
+
await reveal(details, { from: { x: 40, y: 40 }, duration: 500 })
|
|
463
|
+
await conceal(details, { to: { x: 40, y: 40 } })
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
A native circle reveals the view's content or contracts to hide it. Reversing continues from the circle on screen. Existing masks are restored when the effect ends; conceal finishes with `visibility = 'hidden'`, preserving layout space. Reduce Motion applies the final visibility immediately. iOS only for now. See the [circular reveal guide](https://jolt.rafaelaugusto.dev/guide/reveal) for cancellation and layout behavior.
|
|
467
|
+
|
|
468
|
+
## Text
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
import { animateText } from '@rafaelaugusto/jolt'
|
|
472
|
+
|
|
473
|
+
await animateText(title)
|
|
474
|
+
await animateText(title, { by: 'word', stagger: 100 })
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Reveal a Label letter by letter or a word at a time. On iOS, TextKit keeps the complete text layout, including wrapped lines, attributed styles and composed emoji, and Core Animation plays temporary glyph images. On Android, spans that only affect drawing fade and lift each piece under one native animator, keeping the TextView's text, line breaks and truncation. The original Label remains the single accessibility element. Stop restores the full text; Reduce Motion and Android's Remove animations skip the entrance. Rebuild Android apps after upgrading to include the native class. See the [text guide](https://jolt.rafaelaugusto.dev/guide/text) for options and limits.
|
|
478
|
+
|
|
479
|
+
## Rolling numbers
|
|
480
|
+
|
|
481
|
+
```ts
|
|
482
|
+
import { rollTo } from '@rafaelaugusto/jolt'
|
|
483
|
+
|
|
484
|
+
await rollTo(balance, 1249.9, { format: (value) => value.toFixed(2) })
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Change a Label's number and only the digits that differ roll: each one slides out while its replacement slides in on a spring. Positions align on the units digit, so `99` becomes `100` by rolling two digits and adding one. The label holds the final text from the first frame; glyph layers on iOS and a native span animator on Android do the drawing, and tabular figures keep the width stable. Rebuild Android after upgrading. See the [rolling numbers guide](https://jolt.rafaelaugusto.dev/guide/rolling) for options and limits.
|
|
488
|
+
|
|
489
|
+
## Haptics
|
|
490
|
+
|
|
491
|
+
```ts
|
|
492
|
+
animate(card, { y: 0 }, { type: 'spring', haptic: 'light' })
|
|
493
|
+
animate(button, 'bounce', { haptic: { style: 'light', times: [0.6, 1] } })
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Choose `light`, `medium` or `heavy`. By default, one impact fires when the animation visually arrives. Explicit points can mark each landing of a bounce. Delays and stagger follow the animation; interruptions, stopping and unloading cancel pending feedback. iOS only for now. See the [haptics guide](https://jolt.rafaelaugusto.dev/guide/haptics) for timing and device behavior.
|
|
497
|
+
|
|
498
|
+
## Particles and confetti
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
import { confetti, particles } from '@rafaelaugusto/jolt'
|
|
502
|
+
|
|
503
|
+
confetti(button, { count: 80, colors: ['#facc15', '#ef4444', '#3b82f6'] })
|
|
504
|
+
const stopSnow = particles(scene, { source: 'top', angle: 90, velocity: 45 })
|
|
505
|
+
stopSnow() // remove the emitter and its particles
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Confetti bursts into the target's window and cleans itself up. `particles()` adds an emitter to the view for snow, sparks, bubbles or other effects. Both use Core Animation without JavaScript per frame, respect Reduce Motion and stop on unload. iOS only for now. See the [particles guide](https://jolt.rafaelaugusto.dev/guide/particles).
|
|
509
|
+
|
|
510
|
+
## Scroll-linked animations
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
import { scrollLinked } from '@rafaelaugusto/jolt'
|
|
514
|
+
|
|
515
|
+
const unlink = scrollLinked(
|
|
516
|
+
scrollView,
|
|
517
|
+
header,
|
|
518
|
+
{ y: [0, -60], opacity: [1, 0] },
|
|
519
|
+
{ range: [0, 120] },
|
|
520
|
+
)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
A paused native animation follows the ScrollView offset in DIPs, with linear interpolation and clamped progress. Scrolling backward restores earlier frames. Use `axis: 'x'` for horizontal scrolling; descending ranges reverse the mapping. `unlink()` freezes the current frame and removes the binding; unloading either view also cleans it up. Scroll events update progress through JavaScript, so this binding needs the main thread to remain available. See the [guide](https://jolt.rafaelaugusto.dev/guide/scroll-linked).
|
|
524
|
+
|
|
525
|
+
## Follow the keyboard
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
import { followKeyboard } from '@rafaelaugusto/jolt'
|
|
529
|
+
|
|
530
|
+
const release = followKeyboard(composer)
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
The view rises by exactly the distance the keyboard would cover it, with the keyboard's own duration and curve, and settles back when it hides. On iOS the keyboard notifications drive a `UIView` animation; on Android 11+ a native `WindowInsetsAnimation` callback moves the view on every frame, with and without `adjustResize`. The view's `translateY` is written so later animations start from the position on screen. Rebuild Android after upgrading. See the [guide](https://jolt.rafaelaugusto.dev/guide/keyboard).
|
|
534
|
+
|
|
535
|
+
To react in other ways, `onKeyboard()` reports how much of the window the keyboard covers, with a transition that moves in step with it:
|
|
536
|
+
|
|
537
|
+
```ts
|
|
538
|
+
const off = onKeyboard(({ visible, transition }) => {
|
|
539
|
+
animate(header, { scale: visible ? 0.8 : 1 }, transition)
|
|
540
|
+
})
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
`useKeyboard()` from `/vue` keeps a ref in sync until unmount.
|
|
544
|
+
|
|
545
|
+
## Pause, play and scrub
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
const animation = animate(card, 'flipInY')
|
|
549
|
+
animation.pause()
|
|
550
|
+
animation.progress = 0.4
|
|
551
|
+
animation.play()
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`animate()` and `decay()` return a `PlaybackAnimation` with `pause()`, `play()`, `progress`, `paused` and `duration`. Progress runs from 0 to 1 across delays, repeats and stagger. It preserves the paused/playing state; a paused endpoint can scrub backward. Other animations on the same view keep moving. Infinite loops support pause/play but reject progress. Completed and stopped handles remain terminal. Sequence and effect handles keep their existing controls. See [playback](https://jolt.rafaelaugusto.dev/guide/playback) for lifecycle, haptics and gesture examples.
|
|
555
|
+
|
|
556
|
+
## Time scale and reduced motion
|
|
557
|
+
|
|
558
|
+
```ts
|
|
559
|
+
import { configure } from '@rafaelaugusto/jolt'
|
|
560
|
+
|
|
561
|
+
configure({ timeScale: 0.2 }) // everything five times slower
|
|
562
|
+
configure({ reducedMotion: 'always' }) // transforms jump, opacity still fades
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
Tuning a spring at full speed is guesswork. The scale applies to every engine animation started after the call, on iOS and Android alike: durations, delays, stagger, sequence positions and spring time stretch together, keyframes stay dense, and an interruption still picks up the value and velocity on screen. Native loops such as shimmer and loaders keep their own timing.
|
|
566
|
+
|
|
567
|
+
With Reduce Motion on iOS or Remove animations on Android, translations, scale, rotation, paths, decorations and decay jump to their end values while opacity and colors still animate. `'user'` is the default; `'always'` and `'never'` override the platform for the whole library. `reducedMotion()` reads the result, `onReducedMotion(callback)` watches it, and `useReducedMotion()` from `/vue` keeps a ref in sync. See the [guide](https://jolt.rafaelaugusto.dev/guide/configure).
|
|
568
|
+
|
|
244
569
|
## Interrupting and stopping
|
|
245
570
|
|
|
246
571
|
Starting an animation on a property that's already animating takes it over: the new one starts from the value on screen, with the same velocity, so a card can change its mind halfway without a jump. Other properties of the previous animation keep going.
|
|
@@ -259,36 +584,72 @@ Selected properties stay at their current values; the others keep animating. `sc
|
|
|
259
584
|
|
|
260
585
|
If part of an animation is stopped, its `finished` promise resolves with `false` once its remaining properties finish.
|
|
261
586
|
|
|
587
|
+
`set()` places a view without animating it, and stops whatever animates those properties:
|
|
588
|
+
|
|
589
|
+
```ts
|
|
590
|
+
set(logo, { opacity: 1, scale: 1, y: 0 })
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
It takes one value per property, the same properties as `animate()`. The next animation starts from there. See [setting values](https://jolt.rafaelaugusto.dev/guide/interrupting#setting-values-without-animating).
|
|
594
|
+
|
|
262
595
|
## How it works
|
|
263
596
|
|
|
264
|
-
When an animation starts, Jolt resolves where each property starts from (the value on screen, or the keyframes you gave), and computes the whole curve: easing, keyframes, spring physics. It samples the curve 60 times per second of motion, and keeps keyframe times exact. Linear segments
|
|
597
|
+
When an animation starts, Jolt resolves where each property starts from (the value on screen, or the keyframes you gave), and computes the whole curve: easing, keyframes, spring physics and exponential decay. It samples the curve 60 times per second of motion, and keeps keyframe times exact. Linear numeric segments use only their endpoints. Long tween segments and free decays cap sampling at 6,001 keyframes. Color tracks add intermediate OKLab samples even with linear timing.
|
|
265
598
|
|
|
266
599
|
The samples go to the platform in one go:
|
|
267
600
|
|
|
268
601
|
- **iOS**: a `CAKeyframeAnimation` per property on the view's layer (`opacity`, `transform.translation.x`, `transform.scale.y`, `transform.rotation.z`...). Core Animation composes them, so `x` and `scale` can belong to different animations, and plays them in the render server.
|
|
269
602
|
- **Android**: an `ObjectAnimator` per property with `Keyframe`s and a linear interpolator, on the view's `alpha`, `translationX`, `scaleY`, `rotation`...
|
|
270
603
|
|
|
604
|
+
Solid [colors](#colors) use CGColor keyframes on iOS and int keyframes through a native Java target on Android. iOS text uses a UILabel alpha mask so glyph positions stay fixed while the color layer animates.
|
|
605
|
+
|
|
271
606
|
Before playing, the final values are set on the NativeScript view, like `view.animate()` does, so `view.translateX` already holds the target while the animation runs. Stopping writes back the values on screen.
|
|
272
607
|
|
|
273
|
-
|
|
608
|
+
Native playback does not isolate Android View animations from a blocked UI thread. Main-runtime JavaScript shares that thread, and `withLayer()` only caches drawing. Move suitable CPU work to a Worker and keep view updates small. See the [busy JavaScript guide](https://jolt.rafaelaugusto.dev/guide/android-performance) and [Android diagnostic](./benchmarks/android-busy.md).
|
|
609
|
+
|
|
610
|
+
On Android, opacity animations temporarily cache hardware-accelerated containers with children in a hardware layer. Jolt waits for the fade to start and restores the layer on completion, stop, replacement or unload. Existing app-configured layers are preserved; empty, unmeasured and larger-than-screen containers are skipped. See the [fade-layer benchmark](./benchmarks/android-fade.md) for measurement and memory tradeoffs.
|
|
611
|
+
|
|
612
|
+
Each running property remembers its curve and start time. On Android, interruptions read the displayed value and the animator's current play time, so inherited velocity follows the system's animator duration scale. Early spring arrivals use a native timing marker with completion callbacks only; other sequences advance on native completion. Delays, repeats and disabled animations follow the same platform clock, without JavaScript running per frame.
|
|
274
613
|
|
|
275
614
|
## Not there yet
|
|
276
615
|
|
|
277
|
-
-
|
|
278
|
-
-
|
|
616
|
+
- `animate()` supports transforms, opacity, colors, uniform corners and borders, and platform shadows. Size and layout changes ([#21](https://github.com/rafaelaugustos/jolt/issues/21)) aren't animated yet.
|
|
617
|
+
- Gesture composition and nested-scroll handoff remain separate work ([#51](https://github.com/rafaelaugustos/jolt/issues/51)).
|
|
279
618
|
- No layout or shared element transitions ([#21](https://github.com/rafaelaugustos/jolt/issues/21), [#24](https://github.com/rafaelaugustos/jolt/issues/24)).
|
|
280
619
|
|
|
281
620
|
## API
|
|
282
621
|
|
|
283
|
-
**Animating**: `animate(target, keyframes | preset, transition?)`, `stop(target, properties?)`, `sequence(steps)`, `press(target, pressed?, transition?)`.
|
|
622
|
+
**Animating**: `animate(target, keyframes | preset, transition?)`, `decay(target, velocity, options?)`, `stop(target, properties?)`, `sequence(steps)`, `press(target, pressed?, transition?)`, `drag(target, options?)`, `symbolEffect(image, effect, options?)`, `shimmer(target, options?)`, `liquid(image, options?)`, `loader(target, options?)`, `reveal(target, options?)`, `conceal(target, options?)`, `confetti(target, options?)`, `particles(target, options?)`.
|
|
623
|
+
|
|
624
|
+
`animateLayout(container, change?, options?)` animates the next layout of direct children; `motionScope().animateLayout()` owns it through component lifecycle. Cancellation settles temporary offsets into the final layout.
|
|
625
|
+
|
|
626
|
+
`interactionSurface(target)` opts a view/subtree into native touch geometry during animation; returns `{ disposed, dispose() }`. Register before playback; rebuild iOS after upgrading.
|
|
284
627
|
|
|
285
|
-
|
|
628
|
+
`drag()` returns a controller with `position`, `dragging`, `settling`, `cancel()`, `pause()`, `play()` and `dispose()`. `motionScope().drag()` owns it through component lifecycle.
|
|
629
|
+
|
|
630
|
+
`liquid()` returns a `LiquidEffect` with `pause()`, `resume()` and `stop()`.
|
|
631
|
+
|
|
632
|
+
**Animations** from `animate()`, `decay()`, `sequence()`, `symbolEffect()`, `reveal()` and `conceal()`: `await`, `finished`, `stop()`. `press()`, `shimmer()`, `loader()`, `confetti()` and `particles()` return cleanup functions.
|
|
286
633
|
|
|
287
634
|
**Helpers**: `definePreset`, `presets`, `springs`, `cubicBezier`, plus the types (`Keyframes`, `Transition`, `Spring`, `Tween`, `Preset`, `Presets`, `PresetName`, `SpringName`, `Easing`, `Animation`, `Target`...).
|
|
288
635
|
|
|
289
636
|
## Demo
|
|
290
637
|
|
|
291
|
-
[`demo/`](./demo)
|
|
638
|
+
[`demo/`](./demo) opens an interactive assistant on iOS and Android: a fan of document tools, a voice screen with letter-by-letter text, and a staged conversation with document previews and a rolling countdown, with the screens cross-fading through the Vue entry. Open **Help → Watch demo** to replay the flow. The orb is an animated illustration; speech, documents and audio are sample data.
|
|
639
|
+
|
|
640
|
+
| Assistant | Presets | Springs | Stagger |
|
|
641
|
+
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
642
|
+
| <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-assistant.gif" width="200" alt="The assistant replaying its flow on iOS"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-presets.gif" width="200" alt="fadeInUp, popIn and shake"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-springs.gif" width="200" alt="smooth, snappy and bouncy springs on an interruptible ball"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-stagger.gif" width="200" alt="Rows entering with a stagger"> |
|
|
643
|
+
|
|
644
|
+
| Sequences | Press (Android) | Rolling numbers | Text (Android) |
|
|
645
|
+
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
646
|
+
| <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-sequences.gif" width="200" alt="Overlapping steps and steps in order"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-press-android.gif" width="200" alt="Three press effects"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-rolling.gif" width="200" alt="Digits rolling up and down"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-text-android.gif" width="200" alt="Letter and word entrances"> |
|
|
647
|
+
|
|
648
|
+
| Layout (iOS) | Drag and snap (iOS) | Image effects (iOS) | Photo zoom (iOS) |
|
|
649
|
+
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
650
|
+
| <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-layout-ios.gif" width="200" alt="Native cards insert, reorder and expand"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-drag-ios.gif" width="200" alt="A card and panel follow real drags and settle within bounds"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-image-effects-ios.gif" width="200" alt="Ripple, touch distortion and dissolve on a raster image"> | <img src="https://raw.githubusercontent.com/rafaelaugustos/jolt/main/.github/assets/demo-photo-zoom-ios.gif" width="200" alt="A photo responds to pinch, pan and double tap"> |
|
|
651
|
+
|
|
652
|
+
The animation playground opens with `--playground` on iOS and `--es playground true` on Android, with presets, interruptible springs, decay, text, press effects, list rows, the keyboard composer and the native iOS helpers. See the [demo guide](./docs/guide/demo.md) for preview arguments and implementation notes. Every visual feature guide in the [documentation](https://jolt.rafaelaugusto.dev) includes a recording of the real native demo, in English and Portuguese.
|
|
292
653
|
|
|
293
654
|
With the [NativeScript CLI](https://docs.nativescript.org/setup/) installed:
|
|
294
655
|
|
|
@@ -307,10 +668,16 @@ ns run ios # or: ns run android
|
|
|
307
668
|
npm test # unit tests
|
|
308
669
|
npm run typecheck # including the iOS and Android drivers, against the platform typings
|
|
309
670
|
npm run build
|
|
671
|
+
npm run docs:check-media
|
|
672
|
+
npm run docs:build
|
|
310
673
|
```
|
|
311
674
|
|
|
675
|
+
Visual changes require reviewed recordings before completion or release. See [Contributing](./CONTRIBUTING.md).
|
|
676
|
+
|
|
312
677
|
The tests run the engine against a fake platform driver ([`test/helpers.ts`](./test/helpers.ts)) with its own clock: it records the keyframes it would play and ends them when the clock passes their end.
|
|
313
678
|
|
|
679
|
+
For on-device measurements, see the [iOS animation startup benchmark](./benchmarks/README.md).
|
|
680
|
+
|
|
314
681
|
### Releasing
|
|
315
682
|
|
|
316
683
|
1. Bump `version` in `package.json` (`npm version <version> --no-git-tag-version`) and move the changes under it in `CHANGELOG.md`.
|
|
@@ -321,4 +688,4 @@ Publishing authenticates with the `NPM_TOKEN` secret, or with [trusted publishin
|
|
|
321
688
|
|
|
322
689
|
## License
|
|
323
690
|
|
|
324
|
-
[MIT](./LICENSE)
|
|
691
|
+
[MIT](./LICENSE). Maintained by Rafael Augusto and [Cortex Labs](https://getcortexlabs.com/).
|
package/dist/animate.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { PlaybackAnimation } from './playback.js';
|
|
1
2
|
import { View } from '@nativescript/core';
|
|
2
3
|
import { type Animation } from './engine.js';
|
|
3
4
|
import { type PresetName } from './presets.js';
|
|
4
5
|
import { type Property } from './properties.js';
|
|
5
6
|
import { type SpringName } from './transition.js';
|
|
6
|
-
import type { AnimateOptions, Keyframes, Target } from './types.js';
|
|
7
|
+
import type { AnimateOptions, DecayOptions, DecayVelocity, Keyframes, KeyframeProperty, Target, Values } from './types.js';
|
|
7
8
|
/**
|
|
8
9
|
* Animates one view, or several. Takes keyframes or the name of a preset,
|
|
9
10
|
* and a transition or the name of a spring.
|
|
@@ -14,13 +15,28 @@ import type { AnimateOptions, Keyframes, Target } from './types.js';
|
|
|
14
15
|
* animate(rows, 'fadeInUp', { stagger: 40 })
|
|
15
16
|
* ```
|
|
16
17
|
*/
|
|
17
|
-
export declare function animate(target: Target | readonly Target[], animation: Keyframes | PresetName, options?: AnimateOptions | SpringName):
|
|
18
|
+
export declare function animate(target: Target | readonly Target[], animation: Keyframes | PresetName, options?: AnimateOptions | SpringName): PlaybackAnimation;
|
|
19
|
+
/** Validate and snapshot a complete animation before changing any view ownership. */
|
|
20
|
+
export declare function prepareAnimation(target: Target | readonly Target[], animation: Keyframes | PresetName, options?: AnimateOptions | SpringName): () => PlaybackAnimation;
|
|
21
|
+
/** Starts at the current translation and slows down from a velocity in DIPs per second. */
|
|
22
|
+
export declare function decay(target: Target | readonly Target[], velocity: DecayVelocity, options?: DecayOptions): PlaybackAnimation;
|
|
18
23
|
/** Stops all or selected properties at their current values. `scale` stops both axes. */
|
|
19
|
-
export declare function stop(target: Target | readonly Target[], properties?: readonly
|
|
24
|
+
export declare function stop(target: Target | readonly Target[], properties?: readonly KeyframeProperty[]): void;
|
|
25
|
+
/**
|
|
26
|
+
* Applies values at once, stopping whatever animates those properties.
|
|
27
|
+
* `scale` sets both axes. A view that isn't loaded yet gets them right away.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* set(logo, { opacity: 1, scale: 1, y: 0 })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function set(target: Target | readonly Target[], values: Values): void;
|
|
20
34
|
/** Plays several animations as one: finished once they all are, and stopped together. */
|
|
21
35
|
export declare function group(animations: readonly Animation[]): Animation;
|
|
22
36
|
export declare function toView(target: Target): View;
|
|
23
37
|
export declare function toViews(target: Target | readonly Target[]): View[];
|
|
24
38
|
/** Each property the animation touches, with the value it starts from when it sets one. */
|
|
25
39
|
export declare function startingValues(animation: Keyframes | PresetName, options?: AnimateOptions | SpringName): Map<Property, number | null>;
|
|
40
|
+
/** Each property the animation touches, with the value it leaves behind when that is known up front. */
|
|
41
|
+
export declare function endingValues(animation: Keyframes | PresetName, options?: AnimateOptions | SpringName): Map<Property, number | null>;
|
|
26
42
|
//# sourceMappingURL=animate.d.ts.map
|
package/dist/animate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAEtD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAQzC,OAAO,EAKL,KAAK,SAAS,EAEf,MAAM,aAAa,CAAA;AACpB,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzD,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,aAAa,EACb,SAAS,EAET,gBAAgB,EAEhB,MAAM,EACN,MAAM,EACP,MAAM,YAAY,CAAA;AAUnB;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,SAAS,EAAE,SAAS,GAAG,UAAU,EACjC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,GACpC,iBAAiB,CAEnB;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,SAAS,EAAE,SAAS,GAAG,UAAU,EACjC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,GACpC,MAAM,iBAAiB,CAmCzB;AAED,2FAA2F;AAC3F,wBAAgB,KAAK,CACnB,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE,YAAiB,GACzB,iBAAiB,CAkEnB;AAED,yFAAyF;AACzF,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,UAAU,CAAC,EAAE,SAAS,gBAAgB,EAAE,QAMzC;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAc5E;AAED,yFAAyF;AACzF,wBAAgB,KAAK,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,CAWjE;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAO3C;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI,EAAE,CAElE;AA8GD,2FAA2F;AAC3F,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,GAAG,UAAU,EACjC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,gCAUtC;AAED,wGAAwG;AACxG,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,GAAG,UAAU,EACjC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,gCActC"}
|