@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.
Files changed (318) hide show
  1. package/README.md +385 -18
  2. package/dist/animate.d.ts +19 -3
  3. package/dist/animate.d.ts.map +1 -1
  4. package/dist/animate.js +186 -14
  5. package/dist/animate.js.map +1 -1
  6. package/dist/color.d.ts +11 -0
  7. package/dist/color.d.ts.map +1 -0
  8. package/dist/color.js +73 -0
  9. package/dist/color.js.map +1 -0
  10. package/dist/config.d.ts +31 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +73 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/decay.d.ts +11 -0
  15. package/dist/decay.d.ts.map +1 -0
  16. package/dist/decay.js +22 -0
  17. package/dist/decay.js.map +1 -0
  18. package/dist/decoration.d.ts +18 -0
  19. package/dist/decoration.d.ts.map +1 -0
  20. package/dist/decoration.js +155 -0
  21. package/dist/decoration.js.map +1 -0
  22. package/dist/drag-input.d.ts +15 -0
  23. package/dist/drag-input.d.ts.map +1 -0
  24. package/dist/drag-input.js +66 -0
  25. package/dist/drag-input.js.map +1 -0
  26. package/dist/drag-physics.d.ts +20 -0
  27. package/dist/drag-physics.d.ts.map +1 -0
  28. package/dist/drag-physics.js +75 -0
  29. package/dist/drag-physics.js.map +1 -0
  30. package/dist/drag.d.ts +58 -0
  31. package/dist/drag.d.ts.map +1 -0
  32. package/dist/drag.js +324 -0
  33. package/dist/drag.js.map +1 -0
  34. package/dist/driver/android-color.d.ts +20 -0
  35. package/dist/driver/android-color.d.ts.map +1 -0
  36. package/dist/driver/android-color.js +136 -0
  37. package/dist/driver/android-color.js.map +1 -0
  38. package/dist/driver/android-decoration.d.ts +22 -0
  39. package/dist/driver/android-decoration.d.ts.map +1 -0
  40. package/dist/driver/android-decoration.js +139 -0
  41. package/dist/driver/android-decoration.js.map +1 -0
  42. package/dist/driver/android-gradient.d.ts +5 -0
  43. package/dist/driver/android-gradient.d.ts.map +1 -0
  44. package/dist/driver/android-gradient.js +89 -0
  45. package/dist/driver/android-gradient.js.map +1 -0
  46. package/dist/driver/android-playback.d.ts +13 -0
  47. package/dist/driver/android-playback.d.ts.map +1 -0
  48. package/dist/driver/android-playback.js +141 -0
  49. package/dist/driver/android-playback.js.map +1 -0
  50. package/dist/driver/android.d.ts +1 -1
  51. package/dist/driver/android.d.ts.map +1 -1
  52. package/dist/driver/android.js +165 -13
  53. package/dist/driver/android.js.map +1 -1
  54. package/dist/driver/index.d.ts +25 -2
  55. package/dist/driver/index.d.ts.map +1 -1
  56. package/dist/driver/index.js.map +1 -1
  57. package/dist/driver/ios-color.d.ts +16 -0
  58. package/dist/driver/ios-color.d.ts.map +1 -0
  59. package/dist/driver/ios-color.js +392 -0
  60. package/dist/driver/ios-color.js.map +1 -0
  61. package/dist/driver/ios-decoration.d.ts +17 -0
  62. package/dist/driver/ios-decoration.d.ts.map +1 -0
  63. package/dist/driver/ios-decoration.js +271 -0
  64. package/dist/driver/ios-decoration.js.map +1 -0
  65. package/dist/driver/ios-gradient.d.ts +5 -0
  66. package/dist/driver/ios-gradient.d.ts.map +1 -0
  67. package/dist/driver/ios-gradient.js +109 -0
  68. package/dist/driver/ios-gradient.js.map +1 -0
  69. package/dist/driver/ios-layout.d.ts +9 -0
  70. package/dist/driver/ios-layout.d.ts.map +1 -0
  71. package/dist/driver/ios-layout.js +52 -0
  72. package/dist/driver/ios-layout.js.map +1 -0
  73. package/dist/driver/ios-playback.d.ts +10 -0
  74. package/dist/driver/ios-playback.d.ts.map +1 -0
  75. package/dist/driver/ios-playback.js +47 -0
  76. package/dist/driver/ios-playback.js.map +1 -0
  77. package/dist/driver/ios-shadow.d.ts +6 -0
  78. package/dist/driver/ios-shadow.d.ts.map +1 -0
  79. package/dist/driver/ios-shadow.js +52 -0
  80. package/dist/driver/ios-shadow.js.map +1 -0
  81. package/dist/driver/ios.d.ts.map +1 -1
  82. package/dist/driver/ios.js +163 -28
  83. package/dist/driver/ios.js.map +1 -1
  84. package/dist/engine.d.ts +75 -5
  85. package/dist/engine.d.ts.map +1 -1
  86. package/dist/engine.js +293 -23
  87. package/dist/engine.js.map +1 -1
  88. package/dist/gradient.d.ts +90 -0
  89. package/dist/gradient.d.ts.map +1 -0
  90. package/dist/gradient.js +247 -0
  91. package/dist/gradient.js.map +1 -0
  92. package/dist/haptics.d.ts +21 -0
  93. package/dist/haptics.d.ts.map +1 -0
  94. package/dist/haptics.js +120 -0
  95. package/dist/haptics.js.map +1 -0
  96. package/dist/image-effect-native.d.ts +26 -0
  97. package/dist/image-effect-native.d.ts.map +1 -0
  98. package/dist/image-effect-native.js +73 -0
  99. package/dist/image-effect-native.js.map +1 -0
  100. package/dist/image-effect.d.ts +50 -0
  101. package/dist/image-effect.d.ts.map +1 -0
  102. package/dist/image-effect.js +290 -0
  103. package/dist/image-effect.js.map +1 -0
  104. package/dist/image-owner.d.ts +3 -0
  105. package/dist/image-owner.d.ts.map +1 -0
  106. package/dist/image-owner.js +11 -0
  107. package/dist/image-owner.js.map +1 -0
  108. package/dist/index.d.ts +47 -4
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +22 -2
  111. package/dist/index.js.map +1 -1
  112. package/dist/interaction-surface.d.ts +12 -0
  113. package/dist/interaction-surface.d.ts.map +1 -0
  114. package/dist/interaction-surface.js +36 -0
  115. package/dist/interaction-surface.js.map +1 -0
  116. package/dist/items.d.ts +20 -0
  117. package/dist/items.d.ts.map +1 -0
  118. package/dist/items.js +109 -0
  119. package/dist/items.js.map +1 -0
  120. package/dist/keyboard.d.ts +41 -0
  121. package/dist/keyboard.d.ts.map +1 -0
  122. package/dist/keyboard.js +256 -0
  123. package/dist/keyboard.js.map +1 -0
  124. package/dist/label.d.ts +7 -0
  125. package/dist/label.d.ts.map +1 -0
  126. package/dist/label.js +60 -0
  127. package/dist/label.js.map +1 -0
  128. package/dist/layout.d.ts +13 -0
  129. package/dist/layout.d.ts.map +1 -0
  130. package/dist/layout.js +348 -0
  131. package/dist/layout.js.map +1 -0
  132. package/dist/liquid-grid.d.ts +3 -0
  133. package/dist/liquid-grid.d.ts.map +1 -0
  134. package/dist/liquid-grid.js +24 -0
  135. package/dist/liquid-grid.js.map +1 -0
  136. package/dist/liquid.d.ts +18 -0
  137. package/dist/liquid.d.ts.map +1 -0
  138. package/dist/liquid.js +234 -0
  139. package/dist/liquid.js.map +1 -0
  140. package/dist/loader.d.ts +19 -0
  141. package/dist/loader.d.ts.map +1 -0
  142. package/dist/loader.js +173 -0
  143. package/dist/loader.js.map +1 -0
  144. package/dist/motion-scope.d.ts +39 -0
  145. package/dist/motion-scope.d.ts.map +1 -0
  146. package/dist/motion-scope.js +238 -0
  147. package/dist/motion-scope.js.map +1 -0
  148. package/dist/motion-states.d.ts +43 -0
  149. package/dist/motion-states.d.ts.map +1 -0
  150. package/dist/motion-states.js +220 -0
  151. package/dist/motion-states.js.map +1 -0
  152. package/dist/particles.d.ts +39 -0
  153. package/dist/particles.d.ts.map +1 -0
  154. package/dist/particles.js +265 -0
  155. package/dist/particles.js.map +1 -0
  156. package/dist/path.d.ts +12 -0
  157. package/dist/path.d.ts.map +1 -0
  158. package/dist/path.js +90 -0
  159. package/dist/path.js.map +1 -0
  160. package/dist/playback-group.d.ts +6 -0
  161. package/dist/playback-group.d.ts.map +1 -0
  162. package/dist/playback-group.js +141 -0
  163. package/dist/playback-group.js.map +1 -0
  164. package/dist/playback.d.ts +25 -0
  165. package/dist/playback.d.ts.map +1 -0
  166. package/dist/playback.js +35 -0
  167. package/dist/playback.js.map +1 -0
  168. package/dist/press.d.ts.map +1 -1
  169. package/dist/press.js +23 -7
  170. package/dist/press.js.map +1 -1
  171. package/dist/properties.d.ts +6 -3
  172. package/dist/properties.d.ts.map +1 -1
  173. package/dist/properties.js +26 -2
  174. package/dist/properties.js.map +1 -1
  175. package/dist/reveal.d.ts +25 -0
  176. package/dist/reveal.d.ts.map +1 -0
  177. package/dist/reveal.js +294 -0
  178. package/dist/reveal.js.map +1 -0
  179. package/dist/roll-diff.d.ts +22 -0
  180. package/dist/roll-diff.d.ts.map +1 -0
  181. package/dist/roll-diff.js +36 -0
  182. package/dist/roll-diff.js.map +1 -0
  183. package/dist/roll.d.ts +29 -0
  184. package/dist/roll.d.ts.map +1 -0
  185. package/dist/roll.js +442 -0
  186. package/dist/roll.js.map +1 -0
  187. package/dist/scroll-linked.d.ts +10 -0
  188. package/dist/scroll-linked.d.ts.map +1 -0
  189. package/dist/scroll-linked.js +58 -0
  190. package/dist/scroll-linked.js.map +1 -0
  191. package/dist/sequence.d.ts +10 -5
  192. package/dist/sequence.d.ts.map +1 -1
  193. package/dist/sequence.js +208 -14
  194. package/dist/sequence.js.map +1 -1
  195. package/dist/shimmer.d.ts +12 -0
  196. package/dist/shimmer.d.ts.map +1 -0
  197. package/dist/shimmer.js +161 -0
  198. package/dist/shimmer.js.map +1 -0
  199. package/dist/svg-path.d.ts +14 -0
  200. package/dist/svg-path.d.ts.map +1 -0
  201. package/dist/svg-path.js +281 -0
  202. package/dist/svg-path.js.map +1 -0
  203. package/dist/symbol-effect.d.ts +23 -0
  204. package/dist/symbol-effect.d.ts.map +1 -0
  205. package/dist/symbol-effect.js +187 -0
  206. package/dist/symbol-effect.js.map +1 -0
  207. package/dist/text-layout.d.ts +30 -0
  208. package/dist/text-layout.d.ts.map +1 -0
  209. package/dist/text-layout.js +185 -0
  210. package/dist/text-layout.js.map +1 -0
  211. package/dist/text.d.ts +17 -0
  212. package/dist/text.d.ts.map +1 -0
  213. package/dist/text.js +263 -0
  214. package/dist/text.js.map +1 -0
  215. package/dist/track.d.ts +3 -1
  216. package/dist/track.d.ts.map +1 -1
  217. package/dist/track.js +266 -13
  218. package/dist/track.js.map +1 -1
  219. package/dist/transition.d.ts +32 -1
  220. package/dist/transition.d.ts.map +1 -1
  221. package/dist/transition.js +58 -6
  222. package/dist/transition.js.map +1 -1
  223. package/dist/types.d.ts +48 -1
  224. package/dist/types.d.ts.map +1 -1
  225. package/dist/vue/index.d.ts +95 -0
  226. package/dist/vue/index.d.ts.map +1 -0
  227. package/dist/vue/index.js +163 -0
  228. package/dist/vue/index.js.map +1 -0
  229. package/dist/zoom-native.d.ts +30 -0
  230. package/dist/zoom-native.d.ts.map +1 -0
  231. package/dist/zoom-native.js +56 -0
  232. package/dist/zoom-native.js.map +1 -0
  233. package/dist/zoomable.d.ts +48 -0
  234. package/dist/zoomable.d.ts.map +1 -0
  235. package/dist/zoomable.js +249 -0
  236. package/dist/zoomable.js.map +1 -0
  237. package/package.json +29 -5
  238. package/platforms/android/include.gradle +7 -0
  239. package/platforms/android/java/org/rafaelaugusto/jolt/ColorTarget.java +102 -0
  240. package/platforms/android/java/org/rafaelaugusto/jolt/DecorationTarget.java +154 -0
  241. package/platforms/android/java/org/rafaelaugusto/jolt/GradientLayer.java +195 -0
  242. package/platforms/android/java/org/rafaelaugusto/jolt/ImageEffect.java +288 -0
  243. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFollower.java +91 -0
  244. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardObserver.java +97 -0
  245. package/platforms/android/java/org/rafaelaugusto/jolt/NumberRoll.java +190 -0
  246. package/platforms/android/java/org/rafaelaugusto/jolt/PhotoZoom.java +250 -0
  247. package/platforms/android/java/org/rafaelaugusto/jolt/TextReveal.java +180 -0
  248. package/platforms/android/proguard-rules.pro +21 -0
  249. package/platforms/ios/build.xcconfig +1 -0
  250. package/platforms/ios/src/JoltImageEffect.h +25 -0
  251. package/platforms/ios/src/JoltImageEffect.mm +242 -0
  252. package/platforms/ios/src/JoltInteractiveAnimation.h +18 -0
  253. package/platforms/ios/src/JoltInteractiveAnimation.m +123 -0
  254. package/platforms/ios/src/JoltPhotoZoom.h +23 -0
  255. package/platforms/ios/src/JoltPhotoZoom.m +229 -0
  256. package/platforms/ios/src/module.modulemap +6 -0
  257. package/src/android-plugin.d.ts +184 -0
  258. package/src/animate.ts +249 -36
  259. package/src/color.ts +101 -0
  260. package/src/config.ts +84 -0
  261. package/src/decay.ts +45 -0
  262. package/src/decoration.ts +180 -0
  263. package/src/drag-input.ts +74 -0
  264. package/src/drag-physics.ts +110 -0
  265. package/src/drag.ts +395 -0
  266. package/src/driver/android-color.ts +170 -0
  267. package/src/driver/android-decoration.ts +182 -0
  268. package/src/driver/android-gradient.ts +109 -0
  269. package/src/driver/android-playback.ts +157 -0
  270. package/src/driver/android.ts +208 -20
  271. package/src/driver/index.ts +37 -2
  272. package/src/driver/ios-color.ts +441 -0
  273. package/src/driver/ios-decoration.ts +332 -0
  274. package/src/driver/ios-gradient.ts +114 -0
  275. package/src/driver/ios-layout.ts +63 -0
  276. package/src/driver/ios-playback.ts +51 -0
  277. package/src/driver/ios-shadow.ts +50 -0
  278. package/src/driver/ios.ts +193 -33
  279. package/src/engine.ts +321 -27
  280. package/src/gradient.ts +372 -0
  281. package/src/haptics.ts +148 -0
  282. package/src/image-effect-native.ts +129 -0
  283. package/src/image-effect.ts +336 -0
  284. package/src/image-owner.ts +12 -0
  285. package/src/index.ts +75 -4
  286. package/src/interaction-surface.ts +41 -0
  287. package/src/ios-plugin.d.ts +81 -0
  288. package/src/items.ts +140 -0
  289. package/src/keyboard.ts +291 -0
  290. package/src/label.ts +76 -0
  291. package/src/layout.ts +408 -0
  292. package/src/liquid-grid.ts +28 -0
  293. package/src/liquid.ts +287 -0
  294. package/src/loader.ts +202 -0
  295. package/src/motion-scope.ts +279 -0
  296. package/src/motion-states.ts +269 -0
  297. package/src/particles.ts +323 -0
  298. package/src/path.ts +124 -0
  299. package/src/playback-group.ts +143 -0
  300. package/src/playback.ts +62 -0
  301. package/src/press.ts +26 -10
  302. package/src/properties.ts +32 -6
  303. package/src/reveal.ts +374 -0
  304. package/src/roll-diff.ts +57 -0
  305. package/src/roll.ts +515 -0
  306. package/src/scroll-linked.ts +73 -0
  307. package/src/sequence.ts +204 -14
  308. package/src/shimmer.ts +180 -0
  309. package/src/svg-path.ts +355 -0
  310. package/src/symbol-effect.ts +226 -0
  311. package/src/text-layout.ts +228 -0
  312. package/src/text.ts +279 -0
  313. package/src/track.ts +347 -12
  314. package/src/transition.ts +93 -6
  315. package/src/types.ts +49 -1
  316. package/src/vue/index.ts +231 -0
  317. package/src/zoom-native.ts +124 -0
  318. 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
- - [Sequences](#sequences) that start each step once the previous one looks done.
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 | Unit |
62
- | -------------------- | ---------------------------------------- |
63
- | `opacity` | |
64
- | `x`, `y` | DIPs, same as `translateX`, `translateY` |
65
- | `scale` | sets `scaleX` and `scaleY` together |
66
- | `scaleX`, `scaleY` | |
67
- | `rotate` | degrees |
68
- | `rotateX`, `rotateY` | degrees, with perspective |
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 `opacity` eases over 300ms. Keyframes tween.
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 aren't sampled at all.
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
- Each running property remembers its curve and start time, so Jolt knows the value and velocity of any property at any moment without asking the platform. That's what lets an interruption pick up the motion where it was. On Android, where an animator starts on the next frame, it reads the value on screen instead.
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
- - Only transforms and opacity. Colors ([#12](https://github.com/rafaelaugustos/jolt/issues/12)), borders and shadows ([#13](https://github.com/rafaelaugustos/jolt/issues/13)), size and layout changes ([#21](https://github.com/rafaelaugustos/jolt/issues/21)) aren't animated yet.
278
- - No gestures: dragging something and letting it fly with its velocity is next ([#23](https://github.com/rafaelaugustos/jolt/issues/23)).
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
- **Animations** returned by them: `await`, `finished`, `stop()`.
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) is a NativeScript-Vue app with every preset, the three springs on a ball you can interrupt, stagger, press effects, loops and a sequence on launch.
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): Animation;
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 (keyof Keyframes)[]): void;
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
@@ -1 +1 @@
1
- {"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAKL,KAAK,SAAS,EAEf,MAAM,aAAa,CAAA;AACpB,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EAGT,MAAM,EACP,MAAM,YAAY,CAAA;AASnB;;;;;;;;;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,SAAS,CA0BX;AAED,yFAAyF;AACzF,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,SAAS,CAAC,EAAE,QAM1C;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;AAwED,2FAA2F;AAC3F,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,GAAG,UAAU,EACjC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,gCAOtC"}
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"}