@rafaelaugusto/jolt 0.1.0-beta.1 → 0.1.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +367 -18
- package/dist/animate.d.ts +19 -3
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +186 -14
- package/dist/animate.js.map +1 -1
- package/dist/color.d.ts +11 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +73 -0
- package/dist/color.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +73 -0
- package/dist/config.js.map +1 -0
- package/dist/decay.d.ts +11 -0
- package/dist/decay.d.ts.map +1 -0
- package/dist/decay.js +22 -0
- package/dist/decay.js.map +1 -0
- package/dist/decoration.d.ts +18 -0
- package/dist/decoration.d.ts.map +1 -0
- package/dist/decoration.js +155 -0
- package/dist/decoration.js.map +1 -0
- package/dist/drag-input.d.ts +15 -0
- package/dist/drag-input.d.ts.map +1 -0
- package/dist/drag-input.js +66 -0
- package/dist/drag-input.js.map +1 -0
- package/dist/drag-physics.d.ts +20 -0
- package/dist/drag-physics.d.ts.map +1 -0
- package/dist/drag-physics.js +75 -0
- package/dist/drag-physics.js.map +1 -0
- package/dist/drag.d.ts +58 -0
- package/dist/drag.d.ts.map +1 -0
- package/dist/drag.js +324 -0
- package/dist/drag.js.map +1 -0
- package/dist/driver/android-color.d.ts +20 -0
- package/dist/driver/android-color.d.ts.map +1 -0
- package/dist/driver/android-color.js +136 -0
- package/dist/driver/android-color.js.map +1 -0
- package/dist/driver/android-decoration.d.ts +22 -0
- package/dist/driver/android-decoration.d.ts.map +1 -0
- package/dist/driver/android-decoration.js +139 -0
- package/dist/driver/android-decoration.js.map +1 -0
- package/dist/driver/android-playback.d.ts +13 -0
- package/dist/driver/android-playback.d.ts.map +1 -0
- package/dist/driver/android-playback.js +141 -0
- package/dist/driver/android-playback.js.map +1 -0
- package/dist/driver/android.d.ts +1 -1
- package/dist/driver/android.d.ts.map +1 -1
- package/dist/driver/android.js +165 -13
- package/dist/driver/android.js.map +1 -1
- package/dist/driver/index.d.ts +25 -2
- package/dist/driver/index.d.ts.map +1 -1
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/ios-color.d.ts +16 -0
- package/dist/driver/ios-color.d.ts.map +1 -0
- package/dist/driver/ios-color.js +392 -0
- package/dist/driver/ios-color.js.map +1 -0
- package/dist/driver/ios-decoration.d.ts +17 -0
- package/dist/driver/ios-decoration.d.ts.map +1 -0
- package/dist/driver/ios-decoration.js +271 -0
- package/dist/driver/ios-decoration.js.map +1 -0
- package/dist/driver/ios-layout.d.ts +9 -0
- package/dist/driver/ios-layout.d.ts.map +1 -0
- package/dist/driver/ios-layout.js +52 -0
- package/dist/driver/ios-layout.js.map +1 -0
- package/dist/driver/ios-playback.d.ts +10 -0
- package/dist/driver/ios-playback.d.ts.map +1 -0
- package/dist/driver/ios-playback.js +47 -0
- package/dist/driver/ios-playback.js.map +1 -0
- package/dist/driver/ios-shadow.d.ts +6 -0
- package/dist/driver/ios-shadow.d.ts.map +1 -0
- package/dist/driver/ios-shadow.js +52 -0
- package/dist/driver/ios-shadow.js.map +1 -0
- package/dist/driver/ios.d.ts.map +1 -1
- package/dist/driver/ios.js +163 -28
- package/dist/driver/ios.js.map +1 -1
- package/dist/engine.d.ts +75 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +293 -23
- package/dist/engine.js.map +1 -1
- package/dist/haptics.d.ts +21 -0
- package/dist/haptics.d.ts.map +1 -0
- package/dist/haptics.js +120 -0
- package/dist/haptics.js.map +1 -0
- package/dist/image-effect-native.d.ts +26 -0
- package/dist/image-effect-native.d.ts.map +1 -0
- package/dist/image-effect-native.js +73 -0
- package/dist/image-effect-native.js.map +1 -0
- package/dist/image-effect.d.ts +50 -0
- package/dist/image-effect.d.ts.map +1 -0
- package/dist/image-effect.js +290 -0
- package/dist/image-effect.js.map +1 -0
- package/dist/image-owner.d.ts +3 -0
- package/dist/image-owner.d.ts.map +1 -0
- package/dist/image-owner.js +11 -0
- package/dist/image-owner.js.map +1 -0
- package/dist/index.d.ts +43 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/interaction-surface.d.ts +12 -0
- package/dist/interaction-surface.d.ts.map +1 -0
- package/dist/interaction-surface.js +36 -0
- package/dist/interaction-surface.js.map +1 -0
- package/dist/items.d.ts +20 -0
- package/dist/items.d.ts.map +1 -0
- package/dist/items.js +109 -0
- package/dist/items.js.map +1 -0
- package/dist/keyboard.d.ts +41 -0
- package/dist/keyboard.d.ts.map +1 -0
- package/dist/keyboard.js +256 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/label.d.ts +7 -0
- package/dist/label.d.ts.map +1 -0
- package/dist/label.js +60 -0
- package/dist/label.js.map +1 -0
- package/dist/layout.d.ts +13 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +348 -0
- package/dist/layout.js.map +1 -0
- package/dist/liquid-grid.d.ts +3 -0
- package/dist/liquid-grid.d.ts.map +1 -0
- package/dist/liquid-grid.js +24 -0
- package/dist/liquid-grid.js.map +1 -0
- package/dist/liquid.d.ts +18 -0
- package/dist/liquid.d.ts.map +1 -0
- package/dist/liquid.js +234 -0
- package/dist/liquid.js.map +1 -0
- package/dist/loader.d.ts +19 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +173 -0
- package/dist/loader.js.map +1 -0
- package/dist/motion-scope.d.ts +39 -0
- package/dist/motion-scope.d.ts.map +1 -0
- package/dist/motion-scope.js +238 -0
- package/dist/motion-scope.js.map +1 -0
- package/dist/motion-states.d.ts +36 -0
- package/dist/motion-states.d.ts.map +1 -0
- package/dist/motion-states.js +165 -0
- package/dist/motion-states.js.map +1 -0
- package/dist/particles.d.ts +39 -0
- package/dist/particles.d.ts.map +1 -0
- package/dist/particles.js +265 -0
- package/dist/particles.js.map +1 -0
- package/dist/path.d.ts +12 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +90 -0
- package/dist/path.js.map +1 -0
- package/dist/playback-group.d.ts +6 -0
- package/dist/playback-group.d.ts.map +1 -0
- package/dist/playback-group.js +141 -0
- package/dist/playback-group.js.map +1 -0
- package/dist/playback.d.ts +25 -0
- package/dist/playback.d.ts.map +1 -0
- package/dist/playback.js +35 -0
- package/dist/playback.js.map +1 -0
- package/dist/press.d.ts.map +1 -1
- package/dist/press.js +23 -7
- package/dist/press.js.map +1 -1
- package/dist/properties.d.ts +6 -3
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +26 -2
- package/dist/properties.js.map +1 -1
- package/dist/reveal.d.ts +25 -0
- package/dist/reveal.d.ts.map +1 -0
- package/dist/reveal.js +294 -0
- package/dist/reveal.js.map +1 -0
- package/dist/roll-diff.d.ts +22 -0
- package/dist/roll-diff.d.ts.map +1 -0
- package/dist/roll-diff.js +36 -0
- package/dist/roll-diff.js.map +1 -0
- package/dist/roll.d.ts +29 -0
- package/dist/roll.d.ts.map +1 -0
- package/dist/roll.js +442 -0
- package/dist/roll.js.map +1 -0
- package/dist/scroll-linked.d.ts +10 -0
- package/dist/scroll-linked.d.ts.map +1 -0
- package/dist/scroll-linked.js +58 -0
- package/dist/scroll-linked.js.map +1 -0
- package/dist/sequence.d.ts +10 -5
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +208 -14
- package/dist/sequence.js.map +1 -1
- package/dist/shimmer.d.ts +12 -0
- package/dist/shimmer.d.ts.map +1 -0
- package/dist/shimmer.js +161 -0
- package/dist/shimmer.js.map +1 -0
- package/dist/svg-path.d.ts +14 -0
- package/dist/svg-path.d.ts.map +1 -0
- package/dist/svg-path.js +281 -0
- package/dist/svg-path.js.map +1 -0
- package/dist/symbol-effect.d.ts +23 -0
- package/dist/symbol-effect.d.ts.map +1 -0
- package/dist/symbol-effect.js +187 -0
- package/dist/symbol-effect.js.map +1 -0
- package/dist/text-layout.d.ts +30 -0
- package/dist/text-layout.d.ts.map +1 -0
- package/dist/text-layout.js +185 -0
- package/dist/text-layout.js.map +1 -0
- package/dist/text.d.ts +17 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +263 -0
- package/dist/text.js.map +1 -0
- package/dist/track.d.ts +3 -1
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +266 -13
- package/dist/track.js.map +1 -1
- package/dist/transition.d.ts +17 -1
- package/dist/transition.d.ts.map +1 -1
- package/dist/transition.js +41 -6
- package/dist/transition.js.map +1 -1
- package/dist/types.d.ts +48 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vue/index.d.ts +95 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +163 -0
- package/dist/vue/index.js.map +1 -0
- package/dist/zoom-native.d.ts +30 -0
- package/dist/zoom-native.d.ts.map +1 -0
- package/dist/zoom-native.js +56 -0
- package/dist/zoom-native.js.map +1 -0
- package/dist/zoomable.d.ts +48 -0
- package/dist/zoomable.d.ts.map +1 -0
- package/dist/zoomable.js +249 -0
- package/dist/zoomable.js.map +1 -0
- package/package.json +29 -5
- package/platforms/android/include.gradle +7 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ColorTarget.java +102 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/DecorationTarget.java +154 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/ImageEffect.java +288 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFollower.java +91 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardObserver.java +97 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/NumberRoll.java +190 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/PhotoZoom.java +250 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/TextReveal.java +180 -0
- package/platforms/android/proguard-rules.pro +19 -0
- package/platforms/ios/build.xcconfig +1 -0
- package/platforms/ios/src/JoltImageEffect.h +25 -0
- package/platforms/ios/src/JoltImageEffect.mm +242 -0
- package/platforms/ios/src/JoltInteractiveAnimation.h +18 -0
- package/platforms/ios/src/JoltInteractiveAnimation.m +123 -0
- package/platforms/ios/src/JoltPhotoZoom.h +23 -0
- package/platforms/ios/src/JoltPhotoZoom.m +229 -0
- package/platforms/ios/src/module.modulemap +6 -0
- package/src/android-plugin.d.ts +155 -0
- package/src/animate.ts +249 -36
- package/src/color.ts +101 -0
- package/src/config.ts +84 -0
- package/src/decay.ts +45 -0
- package/src/decoration.ts +180 -0
- package/src/drag-input.ts +74 -0
- package/src/drag-physics.ts +110 -0
- package/src/drag.ts +395 -0
- package/src/driver/android-color.ts +170 -0
- package/src/driver/android-decoration.ts +182 -0
- package/src/driver/android-playback.ts +157 -0
- package/src/driver/android.ts +208 -20
- package/src/driver/index.ts +37 -2
- package/src/driver/ios-color.ts +441 -0
- package/src/driver/ios-decoration.ts +332 -0
- package/src/driver/ios-layout.ts +63 -0
- package/src/driver/ios-playback.ts +51 -0
- package/src/driver/ios-shadow.ts +50 -0
- package/src/driver/ios.ts +193 -33
- package/src/engine.ts +321 -27
- package/src/haptics.ts +148 -0
- package/src/image-effect-native.ts +129 -0
- package/src/image-effect.ts +336 -0
- package/src/image-owner.ts +12 -0
- package/src/index.ts +59 -2
- package/src/interaction-surface.ts +41 -0
- package/src/ios-plugin.d.ts +81 -0
- package/src/items.ts +140 -0
- package/src/keyboard.ts +291 -0
- package/src/label.ts +76 -0
- package/src/layout.ts +408 -0
- package/src/liquid-grid.ts +28 -0
- package/src/liquid.ts +287 -0
- package/src/loader.ts +202 -0
- package/src/motion-scope.ts +279 -0
- package/src/motion-states.ts +215 -0
- package/src/particles.ts +323 -0
- package/src/path.ts +124 -0
- package/src/playback-group.ts +143 -0
- package/src/playback.ts +62 -0
- package/src/press.ts +26 -10
- package/src/properties.ts +32 -6
- package/src/reveal.ts +374 -0
- package/src/roll-diff.ts +57 -0
- package/src/roll.ts +515 -0
- package/src/scroll-linked.ts +73 -0
- package/src/sequence.ts +204 -14
- package/src/shimmer.ts +180 -0
- package/src/svg-path.ts +355 -0
- package/src/symbol-effect.ts +226 -0
- package/src/text-layout.ts +228 -0
- package/src/text.ts +279 -0
- package/src/track.ts +347 -12
- package/src/transition.ts +63 -6
- package/src/types.ts +49 -1
- package/src/vue/index.ts +231 -0
- package/src/zoom-native.ts +124 -0
- package/src/zoomable.ts +330 -0
package/src/track.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { clampDecoration, isDecorationProperty } from './decoration.js'
|
|
1
2
|
import { linear } from './easing.js'
|
|
3
|
+
import { solveDecay } from './decay.js'
|
|
4
|
+
import { solveDragRelease } from './drag-physics.js'
|
|
2
5
|
import { precision, type Property } from './properties.js'
|
|
3
|
-
import { solveSpring } from './spring.js'
|
|
6
|
+
import { solveSpring, springPhysics } from './spring.js'
|
|
4
7
|
import type { Curve, ResolvedTransition } from './transition.js'
|
|
8
|
+
import { colorMixer, isColorProperty, type ColorProperty } from './color.js'
|
|
9
|
+
import { reducedMotion, timeScale } from './config.js'
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* One property animated over time. The curve is computed here and sampled,
|
|
@@ -37,18 +42,40 @@ interface Motion {
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
const SAMPLES_PER_SECOND = 60
|
|
45
|
+
const MAX_DECAY_SAMPLES = 6000
|
|
40
46
|
const ARRIVAL_TOLERANCE = 0.05
|
|
41
47
|
|
|
42
48
|
export function createTrack(
|
|
43
49
|
property: Property,
|
|
44
50
|
keyframes: readonly number[],
|
|
45
|
-
|
|
51
|
+
transition: ResolvedTransition,
|
|
46
52
|
velocity: number,
|
|
47
53
|
): Track {
|
|
48
|
-
|
|
54
|
+
if (isColorProperty(property)) return colorTrack(property, keyframes, transition)
|
|
55
|
+
const { curve } = transition
|
|
56
|
+
if (property !== 'opacity' && reducedMotion())
|
|
57
|
+
return instantTrack(
|
|
58
|
+
property,
|
|
59
|
+
restingValue(property, keyframes, curve, transition.repeat, transition.reverse),
|
|
60
|
+
)
|
|
61
|
+
const slowdown = 1 / timeScale()
|
|
62
|
+
const delay = transition.delay * slowdown
|
|
63
|
+
const played = slow(
|
|
49
64
|
curve.type === 'spring'
|
|
50
|
-
? spring(property, keyframes, curve, velocity)
|
|
51
|
-
:
|
|
65
|
+
? spring(property, keyframes, curve, velocity * slowdown, slowdown)
|
|
66
|
+
: curve.type === 'drag'
|
|
67
|
+
? dragMotion(keyframes[0]!, curve, slowdown)
|
|
68
|
+
: curve.type === 'decay'
|
|
69
|
+
? decay(property, keyframes[0]!, curve, slowdown)
|
|
70
|
+
: tween(keyframes, curve, slowdown),
|
|
71
|
+
slowdown,
|
|
72
|
+
)
|
|
73
|
+
const { motion, repeat, reverse } = pauseBetweenRepeats(
|
|
74
|
+
played,
|
|
75
|
+
transition.repeat,
|
|
76
|
+
transition.reverse,
|
|
77
|
+
(transition.repeatDelay ?? 0) * slowdown,
|
|
78
|
+
)
|
|
52
79
|
const { duration } = motion
|
|
53
80
|
const first = motion.values[0]!
|
|
54
81
|
const last = motion.values.at(-1)!
|
|
@@ -65,7 +92,7 @@ export function createTrack(
|
|
|
65
92
|
return reverse && iteration % 2 === 1 ? [duration - local, -1] : [local, 1]
|
|
66
93
|
}
|
|
67
94
|
|
|
68
|
-
|
|
95
|
+
const track: Track = {
|
|
69
96
|
property,
|
|
70
97
|
delay,
|
|
71
98
|
duration,
|
|
@@ -75,7 +102,13 @@ export function createTrack(
|
|
|
75
102
|
values: motion.values,
|
|
76
103
|
to: !finite || endsBackwards ? first : last,
|
|
77
104
|
end,
|
|
78
|
-
arrival:
|
|
105
|
+
arrival:
|
|
106
|
+
(curve.type === 'spring' ||
|
|
107
|
+
curve.type === 'drag' ||
|
|
108
|
+
(curve.type === 'decay' && curve.snap !== undefined)) &&
|
|
109
|
+
transition.repeat === 0
|
|
110
|
+
? delay + arrival(motion)
|
|
111
|
+
: end,
|
|
79
112
|
valueAt: (elapsed) => motion.position(localTime(elapsed)[0]),
|
|
80
113
|
velocityAt: (elapsed) => {
|
|
81
114
|
if (elapsed <= delay || elapsed >= end) return 0
|
|
@@ -83,6 +116,271 @@ export function createTrack(
|
|
|
83
116
|
return motion.velocity(time) * direction
|
|
84
117
|
},
|
|
85
118
|
}
|
|
119
|
+
if (!isDecorationProperty(property)) return track
|
|
120
|
+
return {
|
|
121
|
+
...track,
|
|
122
|
+
values: track.values.map((value) => clampDecoration(property, value)),
|
|
123
|
+
to: clampDecoration(property, track.to),
|
|
124
|
+
valueAt: (elapsed) => clampDecoration(property, track.valueAt(elapsed)),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Already at its end: the engine writes the value and plays nothing. */
|
|
129
|
+
export function instantTrack(property: Property, value: number): Track {
|
|
130
|
+
const to = isDecorationProperty(property) ? clampDecoration(property, value) : value
|
|
131
|
+
return {
|
|
132
|
+
property,
|
|
133
|
+
delay: 0,
|
|
134
|
+
duration: 0,
|
|
135
|
+
repeat: 0,
|
|
136
|
+
reverse: false,
|
|
137
|
+
times: [0, 1],
|
|
138
|
+
values: [to, to],
|
|
139
|
+
to,
|
|
140
|
+
end: 0,
|
|
141
|
+
arrival: 0,
|
|
142
|
+
valueAt: () => to,
|
|
143
|
+
velocityAt: () => 0,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function restingValue(
|
|
148
|
+
property: Property,
|
|
149
|
+
keyframes: readonly number[],
|
|
150
|
+
curve: Curve,
|
|
151
|
+
repeat: number,
|
|
152
|
+
reverse: boolean,
|
|
153
|
+
) {
|
|
154
|
+
const first = keyframes[0]!
|
|
155
|
+
if (!Number.isFinite(repeat) || (reverse && repeat % 2 === 1)) return first
|
|
156
|
+
if (curve.type === 'drag') return solveDragRelease(first, curve).to
|
|
157
|
+
if (curve.type !== 'decay') return keyframes.at(-1)!
|
|
158
|
+
const landing = solveDecay(
|
|
159
|
+
first,
|
|
160
|
+
curve.velocity,
|
|
161
|
+
curve.friction,
|
|
162
|
+
precision[property],
|
|
163
|
+
).to
|
|
164
|
+
return curve.snap ? closest(curve.snap, landing) : landing
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function closest(stops: readonly number[], target: number) {
|
|
168
|
+
return stops.reduce((best, stop) =>
|
|
169
|
+
Math.abs(stop - target) < Math.abs(best - target) ? stop : best,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function colorTrack(
|
|
174
|
+
property: ColorProperty,
|
|
175
|
+
colors: readonly number[],
|
|
176
|
+
transition: ResolvedTransition,
|
|
177
|
+
): Track {
|
|
178
|
+
const progress = createTrack(
|
|
179
|
+
'opacity',
|
|
180
|
+
colors.map((_, i) => i),
|
|
181
|
+
transition,
|
|
182
|
+
0,
|
|
183
|
+
)
|
|
184
|
+
const mixes = colors.slice(1).map((color, i) => colorMixer(colors[i]!, color))
|
|
185
|
+
const colorAt = (position: number) => {
|
|
186
|
+
const p = Math.max(0, Math.min(colors.length - 1, position))
|
|
187
|
+
const segment = Math.min(mixes.length - 1, Math.floor(p))
|
|
188
|
+
return mixes[segment]!(p - segment)
|
|
189
|
+
}
|
|
190
|
+
// Linear timing still needs intermediate colors: native RGB interpolation is
|
|
191
|
+
// only the short bridge between these perceptually spaced samples.
|
|
192
|
+
const steps = Math.max(
|
|
193
|
+
1,
|
|
194
|
+
Math.min(
|
|
195
|
+
MAX_DECAY_SAMPLES,
|
|
196
|
+
Math.ceil((progress.duration / 1000) * SAMPLES_PER_SECOND),
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
const times = [
|
|
200
|
+
...new Set([
|
|
201
|
+
...progress.times,
|
|
202
|
+
...Array.from({ length: steps + 1 }, (_, i) => i / steps),
|
|
203
|
+
]),
|
|
204
|
+
].sort((a, b) => a - b)
|
|
205
|
+
return {
|
|
206
|
+
...progress,
|
|
207
|
+
property,
|
|
208
|
+
times,
|
|
209
|
+
values: times.map((time) =>
|
|
210
|
+
colorAt(
|
|
211
|
+
time === 0
|
|
212
|
+
? progress.values[0]!
|
|
213
|
+
: time === 1
|
|
214
|
+
? progress.values.at(-1)!
|
|
215
|
+
: progress.valueAt(progress.delay + time * progress.duration),
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
to: colorAt(progress.to),
|
|
219
|
+
valueAt: (elapsed) => colorAt(progress.valueAt(elapsed)),
|
|
220
|
+
// Packed colors have no scalar velocity to carry into another spring.
|
|
221
|
+
velocityAt: () => 0,
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Holds the end value for `pause` ms before each repeat, inside the keyframes, so both
|
|
227
|
+
* platforms play it natively. Finite repeats are laid out one after another, with no
|
|
228
|
+
* wait after the last; endless ones loop a cycle that waits at each turn.
|
|
229
|
+
*/
|
|
230
|
+
function pauseBetweenRepeats(
|
|
231
|
+
motion: Motion,
|
|
232
|
+
repeat: number,
|
|
233
|
+
reverse: boolean,
|
|
234
|
+
pause: number,
|
|
235
|
+
): { motion: Motion; repeat: number; reverse: boolean } {
|
|
236
|
+
if (pause <= 0 || repeat <= 0 || motion.duration === 0)
|
|
237
|
+
return { motion, repeat, reverse }
|
|
238
|
+
type Segment = { kind: 'forward' | 'backward' | 'hold'; duration: number }
|
|
239
|
+
const play = (backward: boolean): Segment => ({
|
|
240
|
+
kind: backward ? 'backward' : 'forward',
|
|
241
|
+
duration: motion.duration,
|
|
242
|
+
})
|
|
243
|
+
const hold: Segment = { kind: 'hold', duration: pause }
|
|
244
|
+
const segments: Segment[] = []
|
|
245
|
+
if (Number.isFinite(repeat))
|
|
246
|
+
for (let i = 0; i <= repeat; i++) {
|
|
247
|
+
if (i > 0) segments.push(hold)
|
|
248
|
+
segments.push(play(reverse && i % 2 === 1))
|
|
249
|
+
}
|
|
250
|
+
else segments.push(play(false), hold, ...(reverse ? [play(true), hold] : []))
|
|
251
|
+
|
|
252
|
+
const total = segments.reduce((sum, segment) => sum + segment.duration, 0)
|
|
253
|
+
const starts = segments.map((_, i) =>
|
|
254
|
+
segments.slice(0, i).reduce((sum, segment) => sum + segment.duration, 0),
|
|
255
|
+
)
|
|
256
|
+
const last = motion.values.length - 1
|
|
257
|
+
const endOf = (i: number) => {
|
|
258
|
+
const segment = segments[i]!
|
|
259
|
+
if (segment.kind === 'forward') return motion.values[last]!
|
|
260
|
+
if (segment.kind === 'backward') return motion.values[0]!
|
|
261
|
+
return endOf(i - 1)
|
|
262
|
+
}
|
|
263
|
+
// A loop jumps back to its start at the end of the hold, not across it.
|
|
264
|
+
const jump = Math.min(1, pause / 10)
|
|
265
|
+
|
|
266
|
+
const times: number[] = []
|
|
267
|
+
const values: number[] = []
|
|
268
|
+
segments.forEach((segment, i) => {
|
|
269
|
+
const start = starts[i]!
|
|
270
|
+
if (segment.kind === 'hold') {
|
|
271
|
+
const held = endOf(i - 1)
|
|
272
|
+
const next = segments[i + 1]
|
|
273
|
+
const jumps =
|
|
274
|
+
next !== undefined &&
|
|
275
|
+
(next.kind === 'forward' ? motion.values[0] : motion.values[last]) !== held
|
|
276
|
+
times.push((start + segment.duration - (jumps ? jump : 0)) / total)
|
|
277
|
+
values.push(held)
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
for (let k = 0; k <= last; k++) {
|
|
281
|
+
const index = segment.kind === 'forward' ? k : last - k
|
|
282
|
+
const offset = motion.times[index]!
|
|
283
|
+
const time = segment.kind === 'forward' ? offset : 1 - offset
|
|
284
|
+
if (k === 0 && i > 0 && values.at(-1) === motion.values[index]) continue
|
|
285
|
+
times.push((start + time * motion.duration) / total)
|
|
286
|
+
values.push(motion.values[index]!)
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
const locate = (time: number) => {
|
|
291
|
+
let i = 0
|
|
292
|
+
while (i < segments.length - 1 && time >= starts[i + 1]!) i++
|
|
293
|
+
return [i, time - starts[i]!] as const
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
motion: {
|
|
297
|
+
duration: total,
|
|
298
|
+
times,
|
|
299
|
+
values,
|
|
300
|
+
position: (time) => {
|
|
301
|
+
const [i, local] = locate(Math.max(0, Math.min(total, time)))
|
|
302
|
+
const segment = segments[i]!
|
|
303
|
+
if (segment.kind === 'hold') return endOf(i - 1)
|
|
304
|
+
return motion.position(
|
|
305
|
+
segment.kind === 'forward' ? local : motion.duration - local,
|
|
306
|
+
)
|
|
307
|
+
},
|
|
308
|
+
velocity: (time) => {
|
|
309
|
+
const [i, local] = locate(Math.max(0, Math.min(total, time)))
|
|
310
|
+
const segment = segments[i]!
|
|
311
|
+
if (segment.kind === 'hold') return 0
|
|
312
|
+
return segment.kind === 'forward'
|
|
313
|
+
? motion.velocity(local)
|
|
314
|
+
: -motion.velocity(motion.duration - local)
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
repeat: Number.isFinite(repeat) ? 0 : Infinity,
|
|
318
|
+
reverse: false,
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Plays the same trajectory over a longer or shorter time. */
|
|
323
|
+
function slow(motion: Motion, factor: number): Motion {
|
|
324
|
+
if (factor === 1) return motion
|
|
325
|
+
return {
|
|
326
|
+
...motion,
|
|
327
|
+
duration: motion.duration * factor,
|
|
328
|
+
position: (time) => motion.position(time / factor),
|
|
329
|
+
velocity: (time) => motion.velocity(time / factor) / factor,
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Keyframes per second of playback, so slowed curves stay smooth. */
|
|
334
|
+
function samples(duration: number, density: number) {
|
|
335
|
+
return Math.max(
|
|
336
|
+
1,
|
|
337
|
+
Math.min(
|
|
338
|
+
MAX_DECAY_SAMPLES,
|
|
339
|
+
Math.ceil(((duration * density) / 1000) * SAMPLES_PER_SECOND),
|
|
340
|
+
),
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function dragMotion(
|
|
345
|
+
from: number,
|
|
346
|
+
curve: Extract<Curve, { type: 'drag' }>,
|
|
347
|
+
density: number,
|
|
348
|
+
): Motion {
|
|
349
|
+
const motion = solveDragRelease(from, curve)
|
|
350
|
+
const steps = samples(motion.duration, density)
|
|
351
|
+
const times = Array.from({ length: steps + 1 }, (_, i) => i / steps)
|
|
352
|
+
return {
|
|
353
|
+
...motion,
|
|
354
|
+
times,
|
|
355
|
+
values: times.map((time) => motion.position(time * motion.duration)),
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function decay(
|
|
360
|
+
property: Property,
|
|
361
|
+
from: number,
|
|
362
|
+
curve: Extract<Curve, { type: 'decay' }>,
|
|
363
|
+
density: number,
|
|
364
|
+
): Motion {
|
|
365
|
+
const motion = solveDecay(from, curve.velocity, curve.friction, precision[property])
|
|
366
|
+
if (curve.snap) {
|
|
367
|
+
return spring(
|
|
368
|
+
property,
|
|
369
|
+
[from, closest(curve.snap, motion.to)],
|
|
370
|
+
{ type: 'spring', physics: springPhysics({}), velocity: curve.velocity },
|
|
371
|
+
0,
|
|
372
|
+
density,
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const steps = samples(motion.duration, density)
|
|
377
|
+
const times: number[] = []
|
|
378
|
+
const values: number[] = []
|
|
379
|
+
for (let i = 0; i <= steps; i++) {
|
|
380
|
+
times.push(i / steps)
|
|
381
|
+
values.push(motion.position((i / steps) * motion.duration))
|
|
382
|
+
}
|
|
383
|
+
return { ...motion, times, values }
|
|
86
384
|
}
|
|
87
385
|
|
|
88
386
|
function arrival({ times, values, duration }: Motion) {
|
|
@@ -99,6 +397,7 @@ function spring(
|
|
|
99
397
|
[from, to]: readonly number[],
|
|
100
398
|
curve: Extract<Curve, { type: 'spring' }>,
|
|
101
399
|
velocity: number,
|
|
400
|
+
density: number,
|
|
102
401
|
): Motion {
|
|
103
402
|
const motion = solveSpring(
|
|
104
403
|
curve.physics,
|
|
@@ -107,7 +406,7 @@ function spring(
|
|
|
107
406
|
curve.velocity ?? velocity,
|
|
108
407
|
precision[property],
|
|
109
408
|
)
|
|
110
|
-
const steps =
|
|
409
|
+
const steps = samples(motion.duration, density)
|
|
111
410
|
const times: number[] = []
|
|
112
411
|
const values: number[] = []
|
|
113
412
|
for (let i = 0; i <= steps; i++) {
|
|
@@ -120,7 +419,9 @@ function spring(
|
|
|
120
419
|
function tween(
|
|
121
420
|
keyframes: readonly number[],
|
|
122
421
|
curve: Extract<Curve, { type: 'tween' }>,
|
|
422
|
+
density: number,
|
|
123
423
|
): Motion {
|
|
424
|
+
if (curve.track && curve.track !== linear) return easedTrack(keyframes, curve, density)
|
|
124
425
|
const { duration, ease } = curve
|
|
125
426
|
const offsets = curve.times ?? keyframes.map((_, i) => i / (keyframes.length - 1))
|
|
126
427
|
|
|
@@ -143,10 +444,7 @@ function tween(
|
|
|
143
444
|
const from = keyframes[i]!
|
|
144
445
|
const to = keyframes[i + 1]!
|
|
145
446
|
const easing = ease[i]!
|
|
146
|
-
const steps =
|
|
147
|
-
easing === linear || from === to
|
|
148
|
-
? 1
|
|
149
|
-
: Math.max(1, Math.ceil(((span * duration) / 1000) * SAMPLES_PER_SECOND))
|
|
447
|
+
const steps = easing === linear || from === to ? 1 : samples(span * duration, density)
|
|
150
448
|
for (let step = 1; step <= steps; step++) {
|
|
151
449
|
times.push(start + (span * step) / steps)
|
|
152
450
|
values.push(step === steps ? to : from + (to - from) * easing(step / steps))
|
|
@@ -161,3 +459,40 @@ function tween(
|
|
|
161
459
|
values,
|
|
162
460
|
}
|
|
163
461
|
}
|
|
462
|
+
|
|
463
|
+
/** One easing over the whole timeline, then straight lines between keyframes, sampled evenly in time. */
|
|
464
|
+
function easedTrack(
|
|
465
|
+
keyframes: readonly number[],
|
|
466
|
+
curve: Extract<Curve, { type: 'tween' }>,
|
|
467
|
+
density: number,
|
|
468
|
+
): Motion {
|
|
469
|
+
const { duration, track: ease = linear } = curve
|
|
470
|
+
const offsets = curve.times ?? keyframes.map((_, i) => i / (keyframes.length - 1))
|
|
471
|
+
const last = keyframes.length - 1
|
|
472
|
+
|
|
473
|
+
// Like `interpolate`, an easing past 0 or 1 extends the first or last segment.
|
|
474
|
+
const at = (progress: number) => {
|
|
475
|
+
let i = 0
|
|
476
|
+
while (i < last - 1 && progress > offsets[i + 1]!) i++
|
|
477
|
+
const start = offsets[i]!
|
|
478
|
+
const span = offsets[i + 1]! - start
|
|
479
|
+
const from = keyframes[i]!
|
|
480
|
+
const to = keyframes[i + 1]!
|
|
481
|
+
if (span <= 0) return progress < start ? from : to
|
|
482
|
+
return from + ((to - from) * (progress - start)) / span
|
|
483
|
+
}
|
|
484
|
+
const position = (time: number) =>
|
|
485
|
+
at(ease(duration > 0 ? Math.min(Math.max(time / duration, 0), 1) : 1))
|
|
486
|
+
|
|
487
|
+
const steps = samples(duration, density)
|
|
488
|
+
const times = Array.from({ length: steps + 1 }, (_, i) => i / steps)
|
|
489
|
+
return {
|
|
490
|
+
duration,
|
|
491
|
+
position,
|
|
492
|
+
velocity: (time) => (position(time + 0.5) - position(time - 0.5)) * 1000,
|
|
493
|
+
times,
|
|
494
|
+
values: times.map((time, i) =>
|
|
495
|
+
i === steps ? keyframes[last]! : position(time * duration),
|
|
496
|
+
),
|
|
497
|
+
}
|
|
498
|
+
}
|
package/src/transition.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { resolveEasing, type Easing, type EasingFunction } from './easing.js'
|
|
1
|
+
import { linear, resolveEasing, type Easing, type EasingFunction } from './easing.js'
|
|
2
2
|
import type { Property } from './properties.js'
|
|
3
|
+
import { isDecorationProperty } from './decoration.js'
|
|
4
|
+
import { isColorProperty } from './color.js'
|
|
3
5
|
import { springPhysics, type SpringPhysics } from './spring.js'
|
|
6
|
+
import type { DragCurve } from './drag-physics.js'
|
|
4
7
|
|
|
5
8
|
export interface Timing {
|
|
6
9
|
/** Milliseconds to wait before starting. The first keyframe is applied right away. */
|
|
@@ -9,6 +12,8 @@ export interface Timing {
|
|
|
9
12
|
repeat?: number
|
|
10
13
|
/** `reverse` plays every other repeat backwards. */
|
|
11
14
|
repeatType?: 'loop' | 'reverse'
|
|
15
|
+
/** Milliseconds to hold the end value before each repeat. The first play doesn't wait. */
|
|
16
|
+
repeatDelay?: number
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
export interface Tween extends Timing {
|
|
@@ -19,6 +24,11 @@ export interface Tween extends Timing {
|
|
|
19
24
|
ease?: Easing | readonly Easing[]
|
|
20
25
|
/** Where each keyframe sits, from 0 to 1. Evenly spaced by default. */
|
|
21
26
|
times?: readonly number[]
|
|
27
|
+
/**
|
|
28
|
+
* Run one `ease` over the whole track instead of each segment, with straight lines
|
|
29
|
+
* between keyframes, like React Native's `interpolate`.
|
|
30
|
+
*/
|
|
31
|
+
easeTrack?: boolean
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
export interface Spring extends Timing {
|
|
@@ -45,24 +55,41 @@ export const springs: Record<SpringName, Spring> = {
|
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export type Curve =
|
|
58
|
+
| DragCurve
|
|
48
59
|
| {
|
|
49
60
|
type: 'tween'
|
|
50
61
|
duration: number
|
|
51
62
|
ease: readonly EasingFunction[]
|
|
52
63
|
times: readonly number[] | undefined
|
|
64
|
+
/** One easing over the whole track; `ease` is then linear per segment. */
|
|
65
|
+
track?: EasingFunction
|
|
53
66
|
}
|
|
54
67
|
| { type: 'spring'; physics: SpringPhysics; velocity: number | undefined }
|
|
68
|
+
| {
|
|
69
|
+
type: 'decay'
|
|
70
|
+
friction: number
|
|
71
|
+
velocity: number
|
|
72
|
+
snap: readonly number[] | undefined
|
|
73
|
+
}
|
|
55
74
|
|
|
56
75
|
export interface ResolvedTransition {
|
|
57
76
|
curve: Curve
|
|
58
77
|
delay: number
|
|
59
78
|
repeat: number
|
|
60
79
|
reverse: boolean
|
|
80
|
+
repeatDelay?: number
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
const springKeys = ['stiffness', 'damping', 'mass', 'bounce', 'velocity'] as const
|
|
64
|
-
const curveKeys = [
|
|
65
|
-
|
|
84
|
+
const curveKeys = [
|
|
85
|
+
'type',
|
|
86
|
+
'duration',
|
|
87
|
+
'ease',
|
|
88
|
+
'times',
|
|
89
|
+
'easeTrack',
|
|
90
|
+
...springKeys,
|
|
91
|
+
] as const
|
|
92
|
+
const timingKeys = ['delay', 'repeat', 'repeatType', 'repeatDelay'] as const
|
|
66
93
|
|
|
67
94
|
export const DEFAULT_DURATION = 300
|
|
68
95
|
|
|
@@ -96,7 +123,11 @@ export function resolveTransition(
|
|
|
96
123
|
transition.type ??
|
|
97
124
|
(springKeys.some((key) => (transition as Spring)[key] !== undefined)
|
|
98
125
|
? 'spring'
|
|
99
|
-
: hasCurve(transition) ||
|
|
126
|
+
: hasCurve(transition) ||
|
|
127
|
+
property === 'opacity' ||
|
|
128
|
+
isColorProperty(property) ||
|
|
129
|
+
isDecorationProperty(property) ||
|
|
130
|
+
keyframes > 2
|
|
100
131
|
? 'tween'
|
|
101
132
|
: 'spring')
|
|
102
133
|
|
|
@@ -105,9 +136,19 @@ export function resolveTransition(
|
|
|
105
136
|
if (delay < 0 || Number.isNaN(delay)) throw new RangeError(`Invalid delay: ${delay}.`)
|
|
106
137
|
if (repeat < 0 || Number.isNaN(repeat))
|
|
107
138
|
throw new RangeError(`Invalid repeat: ${repeat}.`)
|
|
108
|
-
const
|
|
139
|
+
const repeatDelay = transition.repeatDelay ?? 0
|
|
140
|
+
if (!Number.isFinite(repeatDelay) || repeatDelay < 0)
|
|
141
|
+
throw new RangeError(`Invalid repeat delay: ${repeatDelay}.`)
|
|
142
|
+
const timing = {
|
|
143
|
+
delay,
|
|
144
|
+
repeat,
|
|
145
|
+
reverse: transition.repeatType === 'reverse',
|
|
146
|
+
...(repeatDelay > 0 && repeat > 0 && { repeatDelay }),
|
|
147
|
+
}
|
|
109
148
|
|
|
110
149
|
if (type === 'spring') {
|
|
150
|
+
if ((transition as Tween).easeTrack)
|
|
151
|
+
throw new TypeError('easeTrack applies to tweens, not springs.')
|
|
111
152
|
if (keyframes > 2) {
|
|
112
153
|
throw new Error(
|
|
113
154
|
`A spring goes from one value to another, but "${property}" has ${keyframes} keyframes. Use a tween for keyframes.`,
|
|
@@ -122,12 +163,21 @@ export function resolveTransition(
|
|
|
122
163
|
|
|
123
164
|
const tween = transition as Tween
|
|
124
165
|
const duration = tween.duration ?? DEFAULT_DURATION
|
|
125
|
-
if (
|
|
166
|
+
if (
|
|
167
|
+
duration < 0 ||
|
|
168
|
+
Number.isNaN(duration) ||
|
|
169
|
+
((isColorProperty(property) || isDecorationProperty(property)) &&
|
|
170
|
+
!Number.isFinite(duration))
|
|
171
|
+
) {
|
|
126
172
|
throw new RangeError(`Invalid duration: ${duration}.`)
|
|
127
173
|
}
|
|
128
174
|
|
|
129
175
|
const segments = keyframes - 1
|
|
130
176
|
const ease = tween.ease ?? 'easeOut'
|
|
177
|
+
if (tween.easeTrack && isEasingList(ease))
|
|
178
|
+
throw new TypeError(
|
|
179
|
+
'easeTrack runs one easing over the whole track; pass a single ease.',
|
|
180
|
+
)
|
|
131
181
|
const eases = isEasingList(ease) ? ease : Array.from({ length: segments }, () => ease)
|
|
132
182
|
if (eases.length !== segments) {
|
|
133
183
|
throw new Error(
|
|
@@ -149,6 +199,13 @@ export function resolveTransition(
|
|
|
149
199
|
}
|
|
150
200
|
}
|
|
151
201
|
|
|
202
|
+
if (tween.easeTrack) {
|
|
203
|
+
const track = resolveEasing(ease as Easing)
|
|
204
|
+
return {
|
|
205
|
+
...timing,
|
|
206
|
+
curve: { type, duration, ease: eases.map(() => linear), times, track },
|
|
207
|
+
}
|
|
208
|
+
}
|
|
152
209
|
return {
|
|
153
210
|
...timing,
|
|
154
211
|
curve: { type, duration, ease: eases.map(resolveEasing), times },
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import type { View } from '@nativescript/core'
|
|
2
|
+
import type { Haptic } from './haptics.js'
|
|
2
3
|
import type { SpringName, Transition } from './transition.js'
|
|
4
|
+
import type { ColorKeyframes } from './color.js'
|
|
3
5
|
|
|
4
6
|
/** A value to animate to, or keyframes. `null` stands for the value the view has when the animation starts. */
|
|
5
7
|
export type KeyframeValue = number | readonly (number | null)[]
|
|
6
8
|
|
|
7
9
|
export interface Keyframes {
|
|
10
|
+
/** One continuous SVG path, expressed as translation coordinates in DIPs. */
|
|
11
|
+
path?: string
|
|
12
|
+
/** Face the path's forward tangent (+X artwork). Requires path. */
|
|
13
|
+
orient?: boolean
|
|
14
|
+
backgroundColor?: ColorKeyframes
|
|
15
|
+
/** Uniform starting edge colors; widths and radii may differ. */
|
|
16
|
+
borderColor?: ColorKeyframes
|
|
17
|
+
color?: ColorKeyframes
|
|
18
|
+
/** Uniform circular corners, in DIPs. */
|
|
19
|
+
borderRadius?: KeyframeValue
|
|
20
|
+
/** Uniform visual border width, in DIPs. Content layout uses the final width. */
|
|
21
|
+
borderWidth?: KeyframeValue
|
|
22
|
+
/** iOS: opacity of one existing boxShadow, from 0 to 1. */
|
|
23
|
+
shadowOpacity?: KeyframeValue
|
|
24
|
+
/** iOS: native blur radius in DIPs (half Core's boxShadow blurRadius). */
|
|
25
|
+
shadowRadius?: KeyframeValue
|
|
26
|
+
/** iOS: offset of one existing boxShadow, in DIPs. */
|
|
27
|
+
shadowOffsetX?: KeyframeValue
|
|
28
|
+
shadowOffsetY?: KeyframeValue
|
|
29
|
+
/** Android: base elevation in DIPs. */
|
|
30
|
+
elevation?: KeyframeValue
|
|
31
|
+
/** Android: dynamic Z offset in DIPs. Native state changes may replace it. */
|
|
32
|
+
translationZ?: KeyframeValue
|
|
8
33
|
opacity?: KeyframeValue
|
|
9
34
|
/** Same as `translateX`, in DIPs. */
|
|
10
35
|
x?: KeyframeValue
|
|
@@ -20,10 +45,31 @@ export interface Keyframes {
|
|
|
20
45
|
rotateY?: KeyframeValue
|
|
21
46
|
}
|
|
22
47
|
|
|
48
|
+
/** Properties that can be timed or stopped independently. A path owns x/y and optionally rotate. */
|
|
49
|
+
export type KeyframeProperty = Exclude<keyof Keyframes, 'path' | 'orient'>
|
|
50
|
+
|
|
51
|
+
/** One value per property, as `set()` applies them. */
|
|
52
|
+
export type Values = {
|
|
53
|
+
[K in KeyframeProperty]?: Exclude<Keyframes[K], readonly unknown[]>
|
|
54
|
+
}
|
|
55
|
+
|
|
23
56
|
/** A view, or anything that holds one: a NativeScript-Vue element, an Angular `ElementRef`. */
|
|
24
57
|
export type Target =
|
|
25
58
|
View | { readonly nativeView: View } | { readonly nativeElement: View }
|
|
26
59
|
|
|
60
|
+
/** Translation velocities in DIPs per second. Omitted axes keep their current motion. */
|
|
61
|
+
export interface DecayVelocity {
|
|
62
|
+
x?: number
|
|
63
|
+
y?: number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface DecayOptions {
|
|
67
|
+
/** Velocity multiplier per millisecond, between 0 and 1 exclusively. Defaults to 0.998. */
|
|
68
|
+
friction?: number
|
|
69
|
+
/** Spring to the closest stop to the projected landing, shared or set per axis. */
|
|
70
|
+
snap?: readonly number[] | { x?: readonly number[]; y?: readonly number[] }
|
|
71
|
+
}
|
|
72
|
+
|
|
27
73
|
export interface Stagger {
|
|
28
74
|
/** Milliseconds between one target and the next. */
|
|
29
75
|
each: number
|
|
@@ -32,10 +78,12 @@ export interface Stagger {
|
|
|
32
78
|
}
|
|
33
79
|
|
|
34
80
|
export type AnimateOptions = Transition & {
|
|
35
|
-
[K in
|
|
81
|
+
[K in KeyframeProperty | 'path']?: Transition | SpringName
|
|
36
82
|
} & {
|
|
37
83
|
/** Offset translations/rotations and multiply scale/opacity by their values at the start. */
|
|
38
84
|
relative?: boolean
|
|
39
85
|
/** With several targets, delays each one by this much more than the previous. */
|
|
40
86
|
stagger?: number | Stagger
|
|
87
|
+
/** iOS impact feedback at arrival, or at explicit points in a finite animation. */
|
|
88
|
+
haptic?: Haptic
|
|
41
89
|
}
|