@symbiote-native/engine 0.1.0

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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/build/accessibility-info/index.android.d.ts +3 -0
  4. package/build/accessibility-info/index.android.js +166 -0
  5. package/build/accessibility-info/index.d.ts +1 -0
  6. package/build/accessibility-info/index.ios.d.ts +3 -0
  7. package/build/accessibility-info/index.ios.js +219 -0
  8. package/build/accessibility-info/index.js +5 -0
  9. package/build/accessibility-info/shared.d.ts +34 -0
  10. package/build/accessibility-info/shared.js +13 -0
  11. package/build/action-sheet-ios/index.d.ts +36 -0
  12. package/build/action-sheet-ios/index.js +74 -0
  13. package/build/alert/index.android.d.ts +5 -0
  14. package/build/alert/index.android.js +117 -0
  15. package/build/alert/index.d.ts +1 -0
  16. package/build/alert/index.ios.d.ts +7 -0
  17. package/build/alert/index.ios.js +83 -0
  18. package/build/alert/index.js +8 -0
  19. package/build/alert/shared.d.ts +19 -0
  20. package/build/alert/shared.js +17 -0
  21. package/build/animated/animated-component-shared.d.ts +5 -0
  22. package/build/animated/animated-component-shared.js +54 -0
  23. package/build/animated/animation.d.ts +9 -0
  24. package/build/animated/animation.js +6 -0
  25. package/build/animated/animations/base.d.ts +27 -0
  26. package/build/animated/animations/base.js +90 -0
  27. package/build/animated/animations/composition.d.ts +38 -0
  28. package/build/animated/animations/composition.js +236 -0
  29. package/build/animated/animations/decay.d.ts +22 -0
  30. package/build/animated/animations/decay.js +65 -0
  31. package/build/animated/animations/raf.d.ts +5 -0
  32. package/build/animated/animations/raf.js +39 -0
  33. package/build/animated/animations/spring-config.d.ts +6 -0
  34. package/build/animated/animations/spring-config.js +55 -0
  35. package/build/animated/animations/spring.d.ts +50 -0
  36. package/build/animated/animations/spring.js +207 -0
  37. package/build/animated/animations/timing.d.ts +27 -0
  38. package/build/animated/animations/timing.js +101 -0
  39. package/build/animated/animations/tracking.d.ts +14 -0
  40. package/build/animated/animations/tracking.js +43 -0
  41. package/build/animated/bezier.d.ts +1 -0
  42. package/build/animated/bezier.js +101 -0
  43. package/build/animated/color.d.ts +37 -0
  44. package/build/animated/color.js +183 -0
  45. package/build/animated/easing.d.ts +20 -0
  46. package/build/animated/easing.js +96 -0
  47. package/build/animated/event.d.ts +36 -0
  48. package/build/animated/event.js +252 -0
  49. package/build/animated/graph.d.ts +38 -0
  50. package/build/animated/graph.js +227 -0
  51. package/build/animated/index.d.ts +20 -0
  52. package/build/animated/index.js +28 -0
  53. package/build/animated/interpolation-node.d.ts +16 -0
  54. package/build/animated/interpolation-node.js +57 -0
  55. package/build/animated/interpolation.d.ts +22 -0
  56. package/build/animated/interpolation.js +199 -0
  57. package/build/animated/mock.d.ts +56 -0
  58. package/build/animated/mock.js +127 -0
  59. package/build/animated/native/native-animated.d.ts +43 -0
  60. package/build/animated/native/native-animated.js +146 -0
  61. package/build/animated/operators.d.ts +80 -0
  62. package/build/animated/operators.js +266 -0
  63. package/build/animated/props.d.ts +20 -0
  64. package/build/animated/props.js +187 -0
  65. package/build/animated/style.d.ts +26 -0
  66. package/build/animated/style.js +187 -0
  67. package/build/animated/value-xy.d.ts +35 -0
  68. package/build/animated/value-xy.js +106 -0
  69. package/build/animated/value.d.ts +36 -0
  70. package/build/animated/value.js +185 -0
  71. package/build/app-registry/index.d.ts +40 -0
  72. package/build/app-registry/index.js +144 -0
  73. package/build/app-state/index.d.ts +16 -0
  74. package/build/app-state/index.js +105 -0
  75. package/build/appearance/index.d.ts +12 -0
  76. package/build/appearance/index.js +84 -0
  77. package/build/back-handler/index.d.ts +14 -0
  78. package/build/back-handler/index.js +106 -0
  79. package/build/commit.d.ts +16 -0
  80. package/build/commit.js +678 -0
  81. package/build/debug.d.ts +5 -0
  82. package/build/debug.js +18 -0
  83. package/build/dimensions/index.d.ts +28 -0
  84. package/build/dimensions/index.js +148 -0
  85. package/build/dispatch.d.ts +2 -0
  86. package/build/dispatch.js +18 -0
  87. package/build/events/index.d.ts +1 -0
  88. package/build/events/index.js +691 -0
  89. package/build/fabric.d.ts +32 -0
  90. package/build/fabric.js +59 -0
  91. package/build/host-instance/index.d.ts +11 -0
  92. package/build/host-instance/index.js +49 -0
  93. package/build/i18n-manager/index.d.ts +13 -0
  94. package/build/i18n-manager/index.js +91 -0
  95. package/build/index.d.ts +80 -0
  96. package/build/index.js +72 -0
  97. package/build/interaction-manager/index.d.ts +45 -0
  98. package/build/interaction-manager/index.js +222 -0
  99. package/build/keyboard/index.d.ts +31 -0
  100. package/build/keyboard/index.js +142 -0
  101. package/build/layout-animation/index.d.ts +66 -0
  102. package/build/layout-animation/index.js +183 -0
  103. package/build/linking/index.android.d.ts +2 -0
  104. package/build/linking/index.android.js +18 -0
  105. package/build/linking/index.d.ts +1 -0
  106. package/build/linking/index.ios.d.ts +2 -0
  107. package/build/linking/index.ios.js +9 -0
  108. package/build/linking/index.js +6 -0
  109. package/build/linking/shared.d.ts +32 -0
  110. package/build/linking/shared.js +98 -0
  111. package/build/native-events.d.ts +24 -0
  112. package/build/native-events.js +129 -0
  113. package/build/native-modules.d.ts +6 -0
  114. package/build/native-modules.js +57 -0
  115. package/build/node.d.ts +36 -0
  116. package/build/node.js +194 -0
  117. package/build/pan-responder/index.d.ts +53 -0
  118. package/build/pan-responder/index.js +353 -0
  119. package/build/permissions-android/index.d.ts +115 -0
  120. package/build/permissions-android/index.js +185 -0
  121. package/build/pixel-ratio/index.d.ts +8 -0
  122. package/build/pixel-ratio/index.js +27 -0
  123. package/build/platform/index.android.d.ts +22 -0
  124. package/build/platform/index.android.js +60 -0
  125. package/build/platform/index.d.ts +1 -0
  126. package/build/platform/index.ios.d.ts +18 -0
  127. package/build/platform/index.ios.js +62 -0
  128. package/build/platform/index.js +5 -0
  129. package/build/platform/shared.d.ts +25 -0
  130. package/build/platform/shared.js +41 -0
  131. package/build/platform-color.d.ts +19 -0
  132. package/build/platform-color.js +25 -0
  133. package/build/post-commit.d.ts +4 -0
  134. package/build/post-commit.js +16 -0
  135. package/build/process-aspect-ratio.d.ts +1 -0
  136. package/build/process-aspect-ratio.js +34 -0
  137. package/build/process-background-image/index.d.ts +28 -0
  138. package/build/process-background-image/index.js +557 -0
  139. package/build/process-box-shadow/index.d.ts +11 -0
  140. package/build/process-box-shadow/index.js +193 -0
  141. package/build/process-filter.d.ts +31 -0
  142. package/build/process-filter.js +304 -0
  143. package/build/process-font-variant.d.ts +1 -0
  144. package/build/process-font-variant.js +17 -0
  145. package/build/process-transform/index.d.ts +5 -0
  146. package/build/process-transform/index.js +120 -0
  147. package/build/process-transform-origin/index.d.ts +3 -0
  148. package/build/process-transform-origin/index.js +108 -0
  149. package/build/registry.d.ts +31 -0
  150. package/build/registry.js +145 -0
  151. package/build/settings/index.d.ts +8 -0
  152. package/build/settings/index.js +126 -0
  153. package/build/share/index.android.d.ts +3 -0
  154. package/build/share/index.android.js +56 -0
  155. package/build/share/index.d.ts +1 -0
  156. package/build/share/index.ios.d.ts +3 -0
  157. package/build/share/index.ios.js +47 -0
  158. package/build/share/index.js +6 -0
  159. package/build/share/shared.d.ts +32 -0
  160. package/build/share/shared.js +32 -0
  161. package/build/status-bar/index.android.d.ts +5 -0
  162. package/build/status-bar/index.android.js +83 -0
  163. package/build/status-bar/index.d.ts +1 -0
  164. package/build/status-bar/index.ios.d.ts +5 -0
  165. package/build/status-bar/index.ios.js +66 -0
  166. package/build/status-bar/index.js +4 -0
  167. package/build/status-bar/shared.d.ts +22 -0
  168. package/build/status-bar/shared.js +22 -0
  169. package/build/style/index.d.ts +1 -0
  170. package/build/style/index.js +30 -0
  171. package/build/style-registry/index.d.ts +11 -0
  172. package/build/style-registry/index.js +165 -0
  173. package/build/style-sheet/index.d.ts +20 -0
  174. package/build/style-sheet/index.js +121 -0
  175. package/build/styles.d.ts +220 -0
  176. package/build/styles.js +7 -0
  177. package/build/surface.d.ts +16 -0
  178. package/build/surface.js +67 -0
  179. package/build/tags.d.ts +1 -0
  180. package/build/tags.js +10 -0
  181. package/build/text-input-state.d.ts +5 -0
  182. package/build/text-input-state.js +29 -0
  183. package/build/toast-android/index.d.ts +10 -0
  184. package/build/toast-android/index.js +108 -0
  185. package/build/vibration/index.android.d.ts +2 -0
  186. package/build/vibration/index.android.js +18 -0
  187. package/build/vibration/index.d.ts +1 -0
  188. package/build/vibration/index.ios.d.ts +2 -0
  189. package/build/vibration/index.ios.js +54 -0
  190. package/build/vibration/index.js +6 -0
  191. package/build/vibration/shared.d.ts +15 -0
  192. package/build/vibration/shared.js +68 -0
  193. package/build/view-config.d.ts +1 -0
  194. package/build/view-config.js +114 -0
  195. package/package.json +41 -0
@@ -0,0 +1,236 @@
1
+ // The driver factory + composition API, ported from RN's
2
+ // AnimatedImplementation.js, JS orchestration only (ADR 0016). `timing` /
3
+ // `spring` / `decay` wrap a value with a fresh driver and return a
4
+ // CompositeAnimation; `parallel` / `sequence` / `stagger` / `loop` / `delay`
5
+ // orchestrate those. Vector (XY/Color) handling, tracking, AnimatedEvent and
6
+ // every native-loop branch are dropped.
7
+ import { dlog } from '../../debug';
8
+ import { isNativeAnimatedAvailable } from '../native/native-animated';
9
+ import { AnimatedNode } from '../graph';
10
+ import { AnimatedValue } from '../value';
11
+ import { AnimatedTracking } from './tracking';
12
+ import { TimingAnimation } from './timing';
13
+ import { SpringAnimation } from './spring';
14
+ import { DecayAnimation } from './decay';
15
+ // A loop offloads to native only when the driver was asked for the native path AND
16
+ // the module is present; otherwise the JS-restart loop must run (a JS timing runs
17
+ // once, it does not loop itself).
18
+ function canOffloadLoop(config) {
19
+ return config.useNativeDriver === true && isNativeAnimatedAvailable();
20
+ }
21
+ // Fold a config's onComplete into the caller's callback so both fire. Mirrors
22
+ // RN's _combineCallbacks.
23
+ function combineCallbacks(callback, config) {
24
+ if (callback !== undefined && config.onComplete !== undefined) {
25
+ const onComplete = config.onComplete;
26
+ return result => {
27
+ onComplete(result);
28
+ callback(result);
29
+ };
30
+ }
31
+ return callback ?? config.onComplete;
32
+ }
33
+ export function timing(value, config) {
34
+ return {
35
+ start(callback) {
36
+ const onEnd = combineCallbacks(callback, config);
37
+ const target = config.toValue;
38
+ if (target instanceof AnimatedNode) {
39
+ value.track(new AnimatedTracking(value, target, toValue => new TimingAnimation({ ...config, toValue }), onEnd));
40
+ }
41
+ else {
42
+ value.animate(new TimingAnimation({ ...config, toValue: target }), onEnd);
43
+ }
44
+ },
45
+ stop() {
46
+ value.stopAnimation();
47
+ },
48
+ reset() {
49
+ value.resetAnimation();
50
+ },
51
+ _nativeLoop(iterations, callback) {
52
+ const target = config.toValue;
53
+ if (target instanceof AnimatedNode || !canOffloadLoop(config))
54
+ return false;
55
+ // One native animation carrying `iterations` runs the loop in native; the
56
+ // completion callback only fires when the count exhausts (never for -1).
57
+ value.animate(new TimingAnimation({ ...config, toValue: target, iterations }), combineCallbacks(callback, config));
58
+ return true;
59
+ },
60
+ };
61
+ }
62
+ export function spring(value, config) {
63
+ return {
64
+ start(callback) {
65
+ const onEnd = combineCallbacks(callback, config);
66
+ const target = config.toValue;
67
+ if (target instanceof AnimatedNode) {
68
+ value.track(new AnimatedTracking(value, target, toValue => new SpringAnimation({ ...config, toValue }), onEnd));
69
+ }
70
+ else {
71
+ value.animate(new SpringAnimation({ ...config, toValue: target }), onEnd);
72
+ }
73
+ },
74
+ _nativeLoop(iterations, callback) {
75
+ const target = config.toValue;
76
+ if (target instanceof AnimatedNode || !canOffloadLoop(config))
77
+ return false;
78
+ value.animate(new SpringAnimation({ ...config, toValue: target, iterations }), combineCallbacks(callback, config));
79
+ return true;
80
+ },
81
+ stop() {
82
+ value.stopAnimation();
83
+ },
84
+ reset() {
85
+ value.resetAnimation();
86
+ },
87
+ };
88
+ }
89
+ export function decay(value, config) {
90
+ return {
91
+ start(callback) {
92
+ value.animate(new DecayAnimation(config), combineCallbacks(callback, config));
93
+ },
94
+ stop() {
95
+ value.stopAnimation();
96
+ },
97
+ reset() {
98
+ value.resetAnimation();
99
+ },
100
+ };
101
+ }
102
+ export function parallel(animations, config) {
103
+ let doneCount = 0;
104
+ // Track per-animation completion so stop() calls each at most once.
105
+ const hasEnded = {};
106
+ const stopTogether = !(config !== undefined && config.stopTogether === false);
107
+ const result = {
108
+ start(callback, isLooping) {
109
+ if (doneCount === animations.length) {
110
+ callback?.({ finished: true });
111
+ return;
112
+ }
113
+ animations.forEach((animation, idx) => {
114
+ const cb = (endResult) => {
115
+ hasEnded[idx] = true;
116
+ doneCount++;
117
+ if (doneCount === animations.length) {
118
+ doneCount = 0;
119
+ callback?.(endResult);
120
+ return;
121
+ }
122
+ if (!endResult.finished && stopTogether) {
123
+ result.stop();
124
+ }
125
+ };
126
+ animation.start(cb, isLooping);
127
+ });
128
+ },
129
+ stop() {
130
+ animations.forEach((animation, idx) => {
131
+ if (!hasEnded[idx])
132
+ animation.stop();
133
+ hasEnded[idx] = true;
134
+ });
135
+ },
136
+ reset() {
137
+ animations.forEach((animation, idx) => {
138
+ animation.reset();
139
+ hasEnded[idx] = false;
140
+ doneCount = 0;
141
+ });
142
+ },
143
+ };
144
+ return result;
145
+ }
146
+ export function sequence(animations) {
147
+ let current = 0;
148
+ return {
149
+ start(callback, isLooping) {
150
+ const onComplete = (result) => {
151
+ if (!result.finished) {
152
+ callback?.(result);
153
+ return;
154
+ }
155
+ current++;
156
+ if (current === animations.length) {
157
+ // A fresh start (without reset) should begin from the top.
158
+ current = 0;
159
+ callback?.(result);
160
+ return;
161
+ }
162
+ animations[current].start(onComplete, isLooping);
163
+ };
164
+ if (animations.length === 0) {
165
+ callback?.({ finished: true });
166
+ }
167
+ else {
168
+ animations[current].start(onComplete, isLooping);
169
+ }
170
+ },
171
+ stop() {
172
+ if (current < animations.length) {
173
+ animations[current].stop();
174
+ }
175
+ },
176
+ reset() {
177
+ animations.forEach((animation, idx) => {
178
+ if (idx <= current)
179
+ animation.reset();
180
+ });
181
+ current = 0;
182
+ },
183
+ };
184
+ }
185
+ export function delay(time) {
186
+ return timing(new AnimatedValue(0), {
187
+ toValue: 0,
188
+ delay: time,
189
+ duration: 0,
190
+ });
191
+ }
192
+ export function stagger(time, animations) {
193
+ return parallel(animations.map((animation, i) => sequence([delay(time * i), animation])));
194
+ }
195
+ export function loop(animation, config = {}) {
196
+ const iterations = config.iterations ?? -1;
197
+ const resetBeforeIteration = config.resetBeforeIteration ?? true;
198
+ let isFinished = false;
199
+ let iterationsSoFar = 0;
200
+ return {
201
+ start(callback) {
202
+ if (iterations === 0) {
203
+ callback?.({ finished: true });
204
+ return;
205
+ }
206
+ // Prefer the native loop: a single native animation runs all iterations in
207
+ // native with zero JS per cycle. Only a single timing/spring offers it; a
208
+ // sequence/parallel returns false (no _nativeLoop) and falls back to JS.
209
+ if (animation._nativeLoop?.(iterations, callback) === true) {
210
+ dlog(`loop: offloaded ${iterations} iterations to native`);
211
+ return;
212
+ }
213
+ const restart = (result = { finished: true }) => {
214
+ if (isFinished || iterationsSoFar === iterations || result.finished === false) {
215
+ callback?.(result);
216
+ }
217
+ else {
218
+ iterationsSoFar++;
219
+ if (resetBeforeIteration)
220
+ animation.reset();
221
+ animation.start(restart, iterations === -1);
222
+ }
223
+ };
224
+ restart();
225
+ },
226
+ stop() {
227
+ isFinished = true;
228
+ animation.stop();
229
+ },
230
+ reset() {
231
+ iterationsSoFar = 0;
232
+ isFinished = false;
233
+ animation.reset();
234
+ },
235
+ };
236
+ }
@@ -0,0 +1,22 @@
1
+ import type { IAnimation, IEndCallback } from '../animation';
2
+ import type { AnimatedValue } from '../value';
3
+ import type { INativeAnimationConfig } from '../native/native-animated';
4
+ import { BaseAnimation, type IAnimationConfig } from './base';
5
+ export interface IDecayAnimationConfig extends IAnimationConfig {
6
+ velocity: number;
7
+ deceleration?: number;
8
+ }
9
+ export declare class DecayAnimation extends BaseAnimation {
10
+ private startTime;
11
+ private lastValue;
12
+ private fromValue;
13
+ private readonly deceleration;
14
+ private readonly velocity;
15
+ private onUpdate;
16
+ private animationFrame;
17
+ constructor(config: IDecayAnimationConfig);
18
+ protected getNativeAnimationConfig(): INativeAnimationConfig;
19
+ start(fromValue: number, onUpdate: (value: number) => void, onEnd: IEndCallback, _previousAnimation: IAnimation | null, animatedValue: AnimatedValue): void;
20
+ private onFrame;
21
+ stop(): void;
22
+ }
@@ -0,0 +1,65 @@
1
+ // DecayAnimation: ported from RN's animations/DecayAnimation.js, JS path only
2
+ // (ADR 0016). Models momentum bleeding off under friction: an initial velocity
3
+ // decays exponentially toward a resting value. Ends when consecutive frames
4
+ // move less than 0.1.
5
+ import { dlog } from '../../debug';
6
+ import { BaseAnimation } from './base';
7
+ import { cancelFrame, requestFrame } from './raf';
8
+ export class DecayAnimation extends BaseAnimation {
9
+ startTime = 0;
10
+ lastValue = 0;
11
+ fromValue = 0;
12
+ deceleration;
13
+ velocity;
14
+ onUpdate = () => { };
15
+ animationFrame = null;
16
+ constructor(config) {
17
+ super(config);
18
+ this.deceleration = config.deceleration ?? 0.998;
19
+ this.velocity = config.velocity;
20
+ }
21
+ getNativeAnimationConfig() {
22
+ return {
23
+ type: 'decay',
24
+ deceleration: this.deceleration,
25
+ velocity: this.velocity,
26
+ iterations: this.__iterations,
27
+ platformConfig: this.__platformConfig,
28
+ debugID: this.__getDebugID(),
29
+ };
30
+ }
31
+ start(fromValue, onUpdate, onEnd, _previousAnimation, animatedValue) {
32
+ this.begin(onEnd);
33
+ this.lastValue = fromValue;
34
+ this.fromValue = fromValue;
35
+ this.onUpdate = onUpdate;
36
+ this.startTime = Date.now();
37
+ if (this.startNativeIfNeeded(animatedValue))
38
+ return;
39
+ this.animationFrame = requestFrame(() => this.onFrame());
40
+ }
41
+ onFrame() {
42
+ const now = Date.now();
43
+ const value = this.fromValue +
44
+ (this.velocity / (1 - this.deceleration)) *
45
+ (1 - Math.exp(-(1 - this.deceleration) * (now - this.startTime)));
46
+ this.onUpdate(value);
47
+ if (Math.abs(this.lastValue - value) < 0.1) {
48
+ this.__notifyAnimationEnd({ finished: true });
49
+ return;
50
+ }
51
+ this.lastValue = value;
52
+ if (this.__active) {
53
+ this.animationFrame = requestFrame(() => this.onFrame());
54
+ }
55
+ }
56
+ stop() {
57
+ super.stop();
58
+ if (this.animationFrame !== null) {
59
+ cancelFrame(this.animationFrame);
60
+ this.animationFrame = null;
61
+ }
62
+ dlog('decay animation stopped');
63
+ this.__notifyAnimationEnd({ finished: false });
64
+ }
65
+ }
@@ -0,0 +1,5 @@
1
+ export declare function requestFrame(callback: () => void): number;
2
+ export declare function cancelFrame(handle: number): void;
3
+ export type ITimerHandle = unknown;
4
+ export declare function setTimer(callback: () => void, delayMs: number): ITimerHandle;
5
+ export declare function clearTimer(handle: ITimerHandle): void;
@@ -0,0 +1,39 @@
1
+ // Drivers read requestAnimationFrame / cancelAnimationFrame from the host at
2
+ // call time rather than importing them: React Native installs them on the
3
+ // global, but a Node headless run may not have them until a smoke polyfills
4
+ // them. Resolving lazily means a polyfill installed after this module loads is
5
+ // still seen. A driver that runs without a host scheduler is a bug, not a
6
+ // silent no-op, so we throw.
7
+ function readGlobal(name) {
8
+ return Reflect.get(globalThis, name);
9
+ }
10
+ export function requestFrame(callback) {
11
+ const raf = readGlobal('requestAnimationFrame');
12
+ if (typeof raf !== 'function') {
13
+ throw new Error('requestAnimationFrame is not available on the host');
14
+ }
15
+ const run = cb => Number(Reflect.apply(raf, globalThis, [cb]));
16
+ return run(callback);
17
+ }
18
+ export function cancelFrame(handle) {
19
+ const caf = readGlobal('cancelAnimationFrame');
20
+ if (typeof caf !== 'function')
21
+ return;
22
+ const run = h => {
23
+ Reflect.apply(caf, globalThis, [h]);
24
+ };
25
+ run(handle);
26
+ }
27
+ export function setTimer(callback, delayMs) {
28
+ const set = readGlobal('setTimeout');
29
+ if (typeof set !== 'function') {
30
+ throw new Error('setTimeout is not available on the host');
31
+ }
32
+ return Reflect.apply(set, globalThis, [callback, delayMs]);
33
+ }
34
+ export function clearTimer(handle) {
35
+ const clear = readGlobal('clearTimeout');
36
+ if (typeof clear !== 'function')
37
+ return;
38
+ Reflect.apply(clear, globalThis, [handle]);
39
+ }
@@ -0,0 +1,6 @@
1
+ export interface ISpringConfigValues {
2
+ stiffness: number;
3
+ damping: number;
4
+ }
5
+ export declare function fromOrigamiTensionAndFriction(tension: number, friction: number): ISpringConfigValues;
6
+ export declare function fromBouncinessAndSpeed(bounciness: number, speed: number): ISpringConfigValues;
@@ -0,0 +1,55 @@
1
+ // Origami spring-parameter conversions, ported verbatim from RN's
2
+ // SpringConfig.js. Maps the designer-friendly bounciness/speed and
3
+ // tension/friction inputs onto the stiffness/damping the damped-oscillator
4
+ // integrator actually consumes. Pure math, no dependencies.
5
+ function stiffnessFromOrigamiValue(oValue) {
6
+ return (oValue - 30) * 3.62 + 194;
7
+ }
8
+ function dampingFromOrigamiValue(oValue) {
9
+ return (oValue - 8) * 3 + 25;
10
+ }
11
+ export function fromOrigamiTensionAndFriction(tension, friction) {
12
+ return {
13
+ stiffness: stiffnessFromOrigamiValue(tension),
14
+ damping: dampingFromOrigamiValue(friction),
15
+ };
16
+ }
17
+ export function fromBouncinessAndSpeed(bounciness, speed) {
18
+ function normalize(value, startValue, endValue) {
19
+ return (value - startValue) / (endValue - startValue);
20
+ }
21
+ function projectNormal(n, start, end) {
22
+ return start + n * (end - start);
23
+ }
24
+ function linearInterpolation(t, start, end) {
25
+ return t * end + (1 - t) * start;
26
+ }
27
+ function quadraticOutInterpolation(t, start, end) {
28
+ return linearInterpolation(2 * t - t * t, start, end);
29
+ }
30
+ function b3Friction1(x) {
31
+ return 0.0007 * Math.pow(x, 3) - 0.031 * Math.pow(x, 2) + 0.64 * x + 1.28;
32
+ }
33
+ function b3Friction2(x) {
34
+ return 0.000044 * Math.pow(x, 3) - 0.006 * Math.pow(x, 2) + 0.36 * x + 2;
35
+ }
36
+ function b3Friction3(x) {
37
+ return 0.00000045 * Math.pow(x, 3) - 0.000332 * Math.pow(x, 2) + 0.1078 * x + 5.84;
38
+ }
39
+ function b3Nobounce(tension) {
40
+ if (tension <= 18)
41
+ return b3Friction1(tension);
42
+ if (tension > 18 && tension <= 44)
43
+ return b3Friction2(tension);
44
+ return b3Friction3(tension);
45
+ }
46
+ let b = normalize(bounciness / 1.7, 0, 20);
47
+ b = projectNormal(b, 0, 0.8);
48
+ const s = normalize(speed / 1.7, 0, 20);
49
+ const bouncyTension = projectNormal(s, 0.5, 200);
50
+ const bouncyFriction = quadraticOutInterpolation(b, b3Nobounce(bouncyTension), 0.01);
51
+ return {
52
+ stiffness: stiffnessFromOrigamiValue(bouncyTension),
53
+ damping: dampingFromOrigamiValue(bouncyFriction),
54
+ };
55
+ }
@@ -0,0 +1,50 @@
1
+ import type { IAnimation, IEndCallback } from '../animation';
2
+ import type { AnimatedValue } from '../value';
3
+ import type { INativeAnimationConfig } from '../native/native-animated';
4
+ import { BaseAnimation, type IAnimationConfig } from './base';
5
+ export interface ISpringAnimationConfig extends IAnimationConfig {
6
+ toValue: number;
7
+ overshootClamping?: boolean;
8
+ restDisplacementThreshold?: number;
9
+ restSpeedThreshold?: number;
10
+ velocity?: number;
11
+ bounciness?: number;
12
+ speed?: number;
13
+ tension?: number;
14
+ friction?: number;
15
+ stiffness?: number;
16
+ damping?: number;
17
+ mass?: number;
18
+ delay?: number;
19
+ }
20
+ interface ISpringInternalState {
21
+ lastPosition: number;
22
+ lastVelocity: number;
23
+ lastTime: number;
24
+ }
25
+ export declare class SpringAnimation extends BaseAnimation {
26
+ private readonly overshootClamping;
27
+ private readonly restDisplacementThreshold;
28
+ private readonly restSpeedThreshold;
29
+ private lastVelocity;
30
+ private startPosition;
31
+ private lastPosition;
32
+ private readonly toValue;
33
+ private readonly stiffness;
34
+ private readonly damping;
35
+ private readonly mass;
36
+ private initialVelocity;
37
+ private readonly delay;
38
+ private lastTime;
39
+ private frameTime;
40
+ private onUpdate;
41
+ private animationFrame;
42
+ private timeout;
43
+ constructor(config: ISpringAnimationConfig);
44
+ getInternalState(): ISpringInternalState;
45
+ protected getNativeAnimationConfig(): INativeAnimationConfig;
46
+ start(fromValue: number, onUpdate: (value: number) => void, onEnd: IEndCallback, previousAnimation: IAnimation | null, animatedValue: AnimatedValue): void;
47
+ private onFrame;
48
+ stop(): void;
49
+ }
50
+ export {};
@@ -0,0 +1,207 @@
1
+ // SpringAnimation: ported from RN's animations/SpringAnimation.js, JS path
2
+ // only (ADR 0016). Integrates the closed form of a damped harmonic oscillator
3
+ // each frame and rests once both velocity and displacement fall below their
4
+ // thresholds. A spring chained after a previous spring inherits its
5
+ // position/velocity/time (getInternalState) so retargeting mid-flight stays
6
+ // continuous. The native-config export is dropped.
7
+ import { dlog } from '../../debug';
8
+ import { BaseAnimation } from './base';
9
+ import { cancelFrame, clearTimer, requestFrame, setTimer } from './raf';
10
+ import { fromBouncinessAndSpeed, fromOrigamiTensionAndFriction } from './spring-config';
11
+ function resolveStiffnessDampingMass(config) {
12
+ if (config.stiffness !== undefined || config.damping !== undefined || config.mass !== undefined) {
13
+ return {
14
+ stiffness: config.stiffness ?? 100,
15
+ damping: config.damping ?? 10,
16
+ mass: config.mass ?? 1,
17
+ };
18
+ }
19
+ if (config.bounciness !== undefined || config.speed !== undefined) {
20
+ const springConfig = fromBouncinessAndSpeed(config.bounciness ?? 8, config.speed ?? 12);
21
+ return { stiffness: springConfig.stiffness, damping: springConfig.damping, mass: 1 };
22
+ }
23
+ const springConfig = fromOrigamiTensionAndFriction(config.tension ?? 40, config.friction ?? 7);
24
+ return { stiffness: springConfig.stiffness, damping: springConfig.damping, mass: 1 };
25
+ }
26
+ export class SpringAnimation extends BaseAnimation {
27
+ overshootClamping;
28
+ restDisplacementThreshold;
29
+ restSpeedThreshold;
30
+ lastVelocity;
31
+ startPosition = 0;
32
+ lastPosition = 0;
33
+ toValue;
34
+ stiffness;
35
+ damping;
36
+ mass;
37
+ initialVelocity;
38
+ delay;
39
+ lastTime = 0;
40
+ frameTime = 0;
41
+ onUpdate = () => { };
42
+ animationFrame = null;
43
+ timeout = null;
44
+ constructor(config) {
45
+ super(config);
46
+ this.overshootClamping = config.overshootClamping ?? false;
47
+ this.restDisplacementThreshold = config.restDisplacementThreshold ?? 0.001;
48
+ this.restSpeedThreshold = config.restSpeedThreshold ?? 0.001;
49
+ this.initialVelocity = config.velocity ?? 0;
50
+ this.lastVelocity = config.velocity ?? 0;
51
+ this.toValue = config.toValue;
52
+ this.delay = config.delay ?? 0;
53
+ const resolved = resolveStiffnessDampingMass(config);
54
+ if (resolved.stiffness <= 0)
55
+ throw new Error('Stiffness value must be greater than 0');
56
+ if (resolved.damping <= 0)
57
+ throw new Error('Damping value must be greater than 0');
58
+ if (resolved.mass <= 0)
59
+ throw new Error('Mass value must be greater than 0');
60
+ this.stiffness = resolved.stiffness;
61
+ this.damping = resolved.damping;
62
+ this.mass = resolved.mass;
63
+ }
64
+ getInternalState() {
65
+ return {
66
+ lastPosition: this.lastPosition,
67
+ lastVelocity: this.lastVelocity,
68
+ lastTime: this.lastTime,
69
+ };
70
+ }
71
+ // Native: hand the oscillator parameters to native (QuartzCore CASpringAnimation).
72
+ getNativeAnimationConfig() {
73
+ return {
74
+ type: 'spring',
75
+ stiffness: this.stiffness,
76
+ damping: this.damping,
77
+ mass: this.mass,
78
+ initialVelocity: this.initialVelocity,
79
+ overshootClamping: this.overshootClamping,
80
+ restDisplacementThreshold: this.restDisplacementThreshold,
81
+ restSpeedThreshold: this.restSpeedThreshold,
82
+ toValue: this.toValue,
83
+ iterations: this.__iterations,
84
+ platformConfig: this.__platformConfig,
85
+ debugID: this.__getDebugID(),
86
+ };
87
+ }
88
+ start(fromValue, onUpdate, onEnd, previousAnimation, animatedValue) {
89
+ this.begin(onEnd);
90
+ this.startPosition = fromValue;
91
+ this.lastPosition = this.startPosition;
92
+ this.onUpdate = onUpdate;
93
+ this.lastTime = Date.now();
94
+ this.frameTime = 0;
95
+ if (previousAnimation instanceof SpringAnimation) {
96
+ const internalState = previousAnimation.getInternalState();
97
+ this.lastPosition = internalState.lastPosition;
98
+ this.lastVelocity = internalState.lastVelocity;
99
+ this.initialVelocity = this.lastVelocity;
100
+ this.lastTime = internalState.lastTime;
101
+ }
102
+ // Native took over → the JS rAF loop is skipped entirely.
103
+ if (this.startNativeIfNeeded(animatedValue))
104
+ return;
105
+ if (this.delay !== 0) {
106
+ this.timeout = setTimer(() => this.onFrame(), this.delay);
107
+ }
108
+ else {
109
+ this.onFrame();
110
+ }
111
+ }
112
+ // This spring model is based off of a damped harmonic oscillator
113
+ // (https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator),
114
+ // using the closed form of the second-order differential equation. It matches
115
+ // the algorithm used by QuartzCore's CASpringAnimation.
116
+ onFrame() {
117
+ // If a lot of frames were lost (a large payload, a paused debugger) advance
118
+ // by at most 4 frames so the spring keeps running fast rather than jumping
119
+ // to the end.
120
+ const MAX_STEPS = 64;
121
+ let now = Date.now();
122
+ if (now > this.lastTime + MAX_STEPS) {
123
+ now = this.lastTime + MAX_STEPS;
124
+ }
125
+ const deltaTime = (now - this.lastTime) / 1000;
126
+ this.frameTime += deltaTime;
127
+ const c = this.damping;
128
+ const m = this.mass;
129
+ const k = this.stiffness;
130
+ const v0 = -this.initialVelocity;
131
+ const zeta = c / (2 * Math.sqrt(k * m));
132
+ const omega0 = Math.sqrt(k / m);
133
+ const omega1 = omega0 * Math.sqrt(1.0 - zeta * zeta);
134
+ const x0 = this.toValue - this.startPosition;
135
+ let position;
136
+ let velocity;
137
+ const t = this.frameTime;
138
+ if (zeta < 1) {
139
+ // Under-damped.
140
+ const envelope = Math.exp(-zeta * omega0 * t);
141
+ position =
142
+ this.toValue -
143
+ envelope *
144
+ (((v0 + zeta * omega0 * x0) / omega1) * Math.sin(omega1 * t) + x0 * Math.cos(omega1 * t));
145
+ velocity =
146
+ zeta *
147
+ omega0 *
148
+ envelope *
149
+ ((Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0)) / omega1 +
150
+ x0 * Math.cos(omega1 * t)) -
151
+ envelope *
152
+ (Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) - omega1 * x0 * Math.sin(omega1 * t));
153
+ }
154
+ else {
155
+ // Critically damped.
156
+ const envelope = Math.exp(-omega0 * t);
157
+ position = this.toValue - envelope * (x0 + (v0 + omega0 * x0) * t);
158
+ velocity = envelope * (v0 * (t * omega0 - 1) + t * x0 * (omega0 * omega0));
159
+ }
160
+ this.lastTime = now;
161
+ this.lastPosition = position;
162
+ this.lastVelocity = velocity;
163
+ this.onUpdate(position);
164
+ if (!this.__active) {
165
+ // A listener may have stopped us inside onUpdate.
166
+ return;
167
+ }
168
+ let isOvershooting = false;
169
+ if (this.overshootClamping && this.stiffness !== 0) {
170
+ if (this.startPosition < this.toValue) {
171
+ isOvershooting = position > this.toValue;
172
+ }
173
+ else {
174
+ isOvershooting = position < this.toValue;
175
+ }
176
+ }
177
+ const isVelocity = Math.abs(velocity) <= this.restSpeedThreshold;
178
+ let isDisplacement = true;
179
+ if (this.stiffness !== 0) {
180
+ isDisplacement = Math.abs(this.toValue - position) <= this.restDisplacementThreshold;
181
+ }
182
+ if (isOvershooting || (isVelocity && isDisplacement)) {
183
+ if (this.stiffness !== 0) {
184
+ // Settle exactly on the target.
185
+ this.lastPosition = this.toValue;
186
+ this.lastVelocity = 0;
187
+ this.onUpdate(this.toValue);
188
+ }
189
+ this.__notifyAnimationEnd({ finished: true });
190
+ return;
191
+ }
192
+ this.animationFrame = requestFrame(() => this.onFrame());
193
+ }
194
+ stop() {
195
+ super.stop();
196
+ if (this.timeout !== null) {
197
+ clearTimer(this.timeout);
198
+ this.timeout = null;
199
+ }
200
+ if (this.animationFrame !== null) {
201
+ cancelFrame(this.animationFrame);
202
+ this.animationFrame = null;
203
+ }
204
+ dlog('spring animation stopped');
205
+ this.__notifyAnimationEnd({ finished: false });
206
+ }
207
+ }