rotor-framework 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +120 -0
- package/package.json +59 -0
- package/src/source/RotorFramework.bs +654 -0
- package/src/source/RotorFrameworkTask.bs +278 -0
- package/src/source/base/BaseModel.bs +52 -0
- package/src/source/base/BasePlugin.bs +48 -0
- package/src/source/base/BaseReducer.bs +184 -0
- package/src/source/base/BaseStack.bs +92 -0
- package/src/source/base/BaseViewModel.bs +124 -0
- package/src/source/base/BaseWidget.bs +104 -0
- package/src/source/base/DispatcherCreator.bs +193 -0
- package/src/source/base/DispatcherExternal.bs +260 -0
- package/src/source/base/ListenerForDispatchers.bs +246 -0
- package/src/source/engine/Constants.bs +74 -0
- package/src/source/engine/animator/Animator.bs +334 -0
- package/src/source/engine/builder/Builder.bs +213 -0
- package/src/source/engine/builder/NodePool.bs +236 -0
- package/src/source/engine/builder/PluginAdapter.bs +139 -0
- package/src/source/engine/builder/PostProcessor.bs +331 -0
- package/src/source/engine/builder/Processor.bs +156 -0
- package/src/source/engine/builder/Tree.bs +278 -0
- package/src/source/engine/builder/TreeBase.bs +313 -0
- package/src/source/engine/builder/WidgetCreate.bs +322 -0
- package/src/source/engine/builder/WidgetRemove.bs +72 -0
- package/src/source/engine/builder/WidgetUpdate.bs +113 -0
- package/src/source/engine/providers/Dispatcher.bs +72 -0
- package/src/source/engine/providers/DispatcherProvider.bs +95 -0
- package/src/source/engine/services/I18n.bs +169 -0
- package/src/source/libs/animate/Animate.bs +753 -0
- package/src/source/libs/animate/LICENSE.txt +21 -0
- package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
- package/src/source/plugins/FieldsPlugin.bs +180 -0
- package/src/source/plugins/FocusPlugin.bs +1522 -0
- package/src/source/plugins/FontStylePlugin.bs +159 -0
- package/src/source/plugins/ObserverPlugin.bs +548 -0
- package/src/source/utils/ArrayUtils.bs +495 -0
- package/src/source/utils/GeneralUtils.bs +181 -0
- package/src/source/utils/NodeUtils.bs +180 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
' Public API
|
|
2
|
+
namespace animate
|
|
3
|
+
function create(params as object) as object
|
|
4
|
+
animation = _animate.create(params)
|
|
5
|
+
return _animate.utils.createNodeFromAA(animation)
|
|
6
|
+
end function
|
|
7
|
+
|
|
8
|
+
' Returns a random integer between min and max, inclusive
|
|
9
|
+
function random(min as integer, max as integer) as integer
|
|
10
|
+
return rnd(max - min) + min
|
|
11
|
+
end function
|
|
12
|
+
|
|
13
|
+
function timeline(params as object) as object
|
|
14
|
+
return new _animate.Timeline(params)
|
|
15
|
+
end function
|
|
16
|
+
|
|
17
|
+
' All of these have been generated with GPT4,
|
|
18
|
+
' hopefully the math is ok :)
|
|
19
|
+
namespace penner
|
|
20
|
+
' The *Sine functions create animations that accelerate
|
|
21
|
+
' or decelerate according to a sinusoidal curve.
|
|
22
|
+
function easeInSine(t as float) as float
|
|
23
|
+
return -1 * cos(t * _animate.utils.constants.radiansFactor) + 1
|
|
24
|
+
end function
|
|
25
|
+
|
|
26
|
+
function easeOutSine(t as float) as float
|
|
27
|
+
return sin(t * _animate.utils.constants.radiansFactor)
|
|
28
|
+
end function
|
|
29
|
+
|
|
30
|
+
function easeInOutSine(t as float) as float
|
|
31
|
+
return -0.5 * (cos(_animate.utils.constants.pi * t) - 1)
|
|
32
|
+
end function
|
|
33
|
+
|
|
34
|
+
function easeOutInSine(t as float) as float
|
|
35
|
+
if (t < 0.5)
|
|
36
|
+
return animate.penner.easeOutSine(t * 2) / 2
|
|
37
|
+
else
|
|
38
|
+
return animate.penner.easeInSine((t - 0.5) * 2) / 2 + 0.5
|
|
39
|
+
end if
|
|
40
|
+
end function
|
|
41
|
+
|
|
42
|
+
' The *Circ functions create an animation that
|
|
43
|
+
' accelerates and decelerates respectively in a manner that resembles
|
|
44
|
+
' a quarter of a circle.
|
|
45
|
+
|
|
46
|
+
function easeInCirc(t as float) as float
|
|
47
|
+
return -1 * (sqr(1 - t * t) - 1)
|
|
48
|
+
end function
|
|
49
|
+
|
|
50
|
+
function easeOutCirc(t as float) as float
|
|
51
|
+
t = t - 1
|
|
52
|
+
return sqr(1 - t * t)
|
|
53
|
+
end function
|
|
54
|
+
|
|
55
|
+
function easeInOutCirc(t as float) as float
|
|
56
|
+
if (t < 0.5)
|
|
57
|
+
return -0.5 * (sqr(1 - 4 * t * t) - 1)
|
|
58
|
+
else
|
|
59
|
+
t = t - 1
|
|
60
|
+
return 0.5 * (sqr(1 - 4 * t * t) + 1)
|
|
61
|
+
end if
|
|
62
|
+
end function
|
|
63
|
+
|
|
64
|
+
function easeOutInCirc(t as float) as float
|
|
65
|
+
if (t < 0.5)
|
|
66
|
+
return animate.penner.easeOutCirc(t * 2) / 2
|
|
67
|
+
else
|
|
68
|
+
return animate.penner.easeInCirc((t - 0.5) * 2) / 2 + 0.5
|
|
69
|
+
end if
|
|
70
|
+
end function
|
|
71
|
+
|
|
72
|
+
' The *Elastic functions create animations that overshoot
|
|
73
|
+
' the final state and then oscillate around it before settling,
|
|
74
|
+
' creating an elastic effect.
|
|
75
|
+
' `p` is the period of the oscillation
|
|
76
|
+
' `s` is the phase shift of the oscillation
|
|
77
|
+
' These values have been chosen empirically.
|
|
78
|
+
|
|
79
|
+
function easeInElastic(t as Float) as Float
|
|
80
|
+
if (t = 0)
|
|
81
|
+
return 0
|
|
82
|
+
end if
|
|
83
|
+
if (t = 1)
|
|
84
|
+
return 1
|
|
85
|
+
end if
|
|
86
|
+
p = 0.3
|
|
87
|
+
s = p / 4
|
|
88
|
+
t = t - 1
|
|
89
|
+
return -(2 ^ (10 * t) * sin((t - s) * (2 * _animate.utils.constants.pi) / p))
|
|
90
|
+
end function
|
|
91
|
+
|
|
92
|
+
function easeOutElastic(t as Float) as Float
|
|
93
|
+
if (t = 0)
|
|
94
|
+
return 0
|
|
95
|
+
end if
|
|
96
|
+
if (t = 1)
|
|
97
|
+
return 1
|
|
98
|
+
end if
|
|
99
|
+
p = 0.3
|
|
100
|
+
s = p / 4
|
|
101
|
+
return 2 ^ (-10 * t) * sin((t - s) * (2 * _animate.utils.constants.pi) / p) + 1
|
|
102
|
+
end function
|
|
103
|
+
|
|
104
|
+
function easeInOutElastic(t as Float) as Float
|
|
105
|
+
if (t = 0)
|
|
106
|
+
return 0
|
|
107
|
+
end if
|
|
108
|
+
if (t = 1)
|
|
109
|
+
return 1
|
|
110
|
+
end if
|
|
111
|
+
p = 0.45
|
|
112
|
+
s = p / 4
|
|
113
|
+
if (t < 0.5)
|
|
114
|
+
t = t - 1
|
|
115
|
+
return -0.5 * (2 ^ (10 * t) * sin((t - s) * (2 * _animate.utils.constants.pi) / p))
|
|
116
|
+
else
|
|
117
|
+
t = t - 1
|
|
118
|
+
return 0.5 * (2 ^ (-10 * t) * sin((t - s) * (2 * _animate.utils.constants.pi) / p)) + 1
|
|
119
|
+
end if
|
|
120
|
+
end function
|
|
121
|
+
|
|
122
|
+
function easeOutInElastic(t as Float) as Float
|
|
123
|
+
if (t < 0.5)
|
|
124
|
+
return animate.penner.easeOutElastic(t * 2) / 2
|
|
125
|
+
else
|
|
126
|
+
return animate.penner.easeInElastic((t - 0.5) * 2) / 2 + 0.5
|
|
127
|
+
end if
|
|
128
|
+
end function
|
|
129
|
+
|
|
130
|
+
' The *Back functions create an animation
|
|
131
|
+
' that overshoots the final state and then comes back.
|
|
132
|
+
' This creates a "pullback" or "draw back" effect at the beginning
|
|
133
|
+
' or end of the animation, respectively.
|
|
134
|
+
' `s` is a constant that determines the amount of overshoot.
|
|
135
|
+
' The value 1.70158 results in about a 10% overshoot.
|
|
136
|
+
|
|
137
|
+
function easeInBack(t as float) as float
|
|
138
|
+
s = 1.70158
|
|
139
|
+
return t * t * ((s + 1) * t - s)
|
|
140
|
+
end function
|
|
141
|
+
|
|
142
|
+
function easeOutBack(t as float) as float
|
|
143
|
+
s = 1.70158
|
|
144
|
+
t = t - 1
|
|
145
|
+
return (t * t * ((s + 1) * t + s) + 1)
|
|
146
|
+
end function
|
|
147
|
+
|
|
148
|
+
function easeInOutBack(t as float) as float
|
|
149
|
+
' Adjust the overshoot to achieve a visually
|
|
150
|
+
' pleasing result for the combined "in" and "out" animation.
|
|
151
|
+
s = 1.70158 * 1.525
|
|
152
|
+
if (t < 0.5)
|
|
153
|
+
return (t * t * ((s + 1) * t - s)) / 2
|
|
154
|
+
else
|
|
155
|
+
t = t - 2
|
|
156
|
+
return (t * t * ((s + 1) * t + s) + 2) / 2
|
|
157
|
+
end if
|
|
158
|
+
end function
|
|
159
|
+
|
|
160
|
+
function easeOutInBack(t as float) as float
|
|
161
|
+
if (t < 0.5)
|
|
162
|
+
return animate.penner.easeOutBack(t * 2) / 2
|
|
163
|
+
else
|
|
164
|
+
return animate.penner.easeInBack((t - 0.5) * 2) / 2 + 0.5
|
|
165
|
+
end if
|
|
166
|
+
end function
|
|
167
|
+
|
|
168
|
+
' The *Bounce functions create animations that mimic a bouncing effect.
|
|
169
|
+
' The constants in the function (7.5625, 2.75, etc.)
|
|
170
|
+
' are chosen empirically to make the bounce effect look good.
|
|
171
|
+
|
|
172
|
+
function easeInBounce(t as float) as float
|
|
173
|
+
return 1 - animate.penner.easeOutBounce(1 - t)
|
|
174
|
+
end function
|
|
175
|
+
|
|
176
|
+
function easeOutBounce(t as float) as float
|
|
177
|
+
if (t < (1 / 2.75))
|
|
178
|
+
return (7.5625 * t * t)
|
|
179
|
+
else if (t < (2 / 2.75))
|
|
180
|
+
t = t - (1.5 / 2.75)
|
|
181
|
+
return (7.5625 * t * t + 0.75)
|
|
182
|
+
else if (t < (2.5 / 2.75))
|
|
183
|
+
t = t - (2.25 / 2.75)
|
|
184
|
+
return (7.5625 * t * t + 0.9375)
|
|
185
|
+
else
|
|
186
|
+
t = t - (2.625 / 2.75)
|
|
187
|
+
return (7.5625 * t * t + 0.984375)
|
|
188
|
+
end if
|
|
189
|
+
end function
|
|
190
|
+
|
|
191
|
+
function easeInOutBounce(t as float) as float
|
|
192
|
+
if (t < 0.5)
|
|
193
|
+
return animate.penner.easeInBounce(t * 2) / 2
|
|
194
|
+
else
|
|
195
|
+
return animate.penner.easeOutBounce(t * 2 - 1) / 2 + 0.5
|
|
196
|
+
end if
|
|
197
|
+
end function
|
|
198
|
+
|
|
199
|
+
function easeOutInBounce(t as float) as float
|
|
200
|
+
if (t < 0.5)
|
|
201
|
+
return animate.penner.easeOutBounce(t * 2) / 2
|
|
202
|
+
else
|
|
203
|
+
return animate.penner.easeInBounce((t - 0.5) * 2) / 2 + 0.5
|
|
204
|
+
end if
|
|
205
|
+
end function
|
|
206
|
+
end namespace
|
|
207
|
+
end namespace
|
|
208
|
+
|
|
209
|
+
' Private methods
|
|
210
|
+
namespace _animate
|
|
211
|
+
function create(params as object, recursive = true as boolean) as object
|
|
212
|
+
animations = []
|
|
213
|
+
animationParams = _animate.utils.extractAnimationParams(params)
|
|
214
|
+
if (animationParams.duration = invalid)
|
|
215
|
+
animationParams.duration = 1
|
|
216
|
+
end if
|
|
217
|
+
|
|
218
|
+
for each animationKey in animationParams.keys()
|
|
219
|
+
params.delete(animationKey)
|
|
220
|
+
end for
|
|
221
|
+
|
|
222
|
+
interpolatorParams = _animate.utils.extractInterpolatorParams(params)
|
|
223
|
+
for each interpolatorKey in interpolatorParams.keys()
|
|
224
|
+
params.delete(interpolatorKey)
|
|
225
|
+
end for
|
|
226
|
+
|
|
227
|
+
targets = []
|
|
228
|
+
if (params.target <> invalid)
|
|
229
|
+
targets = params.target
|
|
230
|
+
params.delete("target")
|
|
231
|
+
end if
|
|
232
|
+
|
|
233
|
+
if (params.targets <> invalid)
|
|
234
|
+
targets = params.targets
|
|
235
|
+
params.delete("targets")
|
|
236
|
+
end if
|
|
237
|
+
|
|
238
|
+
autoplay = params.autoplay
|
|
239
|
+
if (autoplay = invalid)
|
|
240
|
+
autoplay = false
|
|
241
|
+
end if
|
|
242
|
+
params.delete("autoplay")
|
|
243
|
+
|
|
244
|
+
nodeTargets = getNodeTargets(targets)
|
|
245
|
+
|
|
246
|
+
subAnimations = []
|
|
247
|
+
|
|
248
|
+
if (recursive)
|
|
249
|
+
subKeys = []
|
|
250
|
+
|
|
251
|
+
for each item in params.items()
|
|
252
|
+
if (_animate.is._aa(item.value))
|
|
253
|
+
subParams = {}
|
|
254
|
+
subParams.append(animationParams)
|
|
255
|
+
subParams.append(interpolatorParams)
|
|
256
|
+
subParams.targets = nodeTargets
|
|
257
|
+
subParams[item.key] = item.value.value
|
|
258
|
+
item.value.delete("value")
|
|
259
|
+
subParams.append(item.value)
|
|
260
|
+
subAnimation = _animate.create(subParams, false)
|
|
261
|
+
subAnimations.push(_animate.utils.shallowCopy(subAnimation))
|
|
262
|
+
subKeys.push(item.key)
|
|
263
|
+
end if
|
|
264
|
+
end for
|
|
265
|
+
|
|
266
|
+
for each subKey in subKeys
|
|
267
|
+
params.delete(subKey)
|
|
268
|
+
end for
|
|
269
|
+
end if
|
|
270
|
+
|
|
271
|
+
customEaseFunction = invalid
|
|
272
|
+
if (_animate.is.func(animationParams.easeFunction))
|
|
273
|
+
customEaseFunction = animationParams.easeFunction
|
|
274
|
+
animationParams.easeFunction = "linear"
|
|
275
|
+
end if
|
|
276
|
+
|
|
277
|
+
targetsLength = nodeTargets.count()
|
|
278
|
+
for i = 0 to targetsLength - 1
|
|
279
|
+
nodeTarget = nodeTargets[i]
|
|
280
|
+
if (nodeTarget.id = invalid or nodeTarget.id = "")
|
|
281
|
+
nodeTarget.id = `__animate_${rnd(42069)}` ' nice
|
|
282
|
+
end if
|
|
283
|
+
|
|
284
|
+
targetInfo = {
|
|
285
|
+
nodeTarget: nodeTarget,
|
|
286
|
+
index: i,
|
|
287
|
+
targetsLength: targetsLength
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
finalAnimationParams = _animate.utils.getFunctionValueParams(animationParams, targetInfo)
|
|
291
|
+
finalInterpolatorParams = _animate.utils.getFunctionValueParams(interpolatorParams, targetInfo)
|
|
292
|
+
finalParams = _animate.utils.getFunctionValueParams(params, targetInfo)
|
|
293
|
+
|
|
294
|
+
alternate = (finalParams.direction = "alternate")
|
|
295
|
+
if (alternate)
|
|
296
|
+
finalParams.direction = "normal"
|
|
297
|
+
end if
|
|
298
|
+
|
|
299
|
+
animation = {
|
|
300
|
+
subtype: _animate.utils.nodes.Animation,
|
|
301
|
+
children: _animate.getTargetInterpolators(nodeTarget, finalParams, _animate.utils.shallowCopy(finalInterpolatorParams), finalAnimationParams.duration, customEaseFunction)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (animation.children.count() > 0)
|
|
305
|
+
if (alternate)
|
|
306
|
+
alternatedParams = _animate.utils.shallowCopy(finalParams)
|
|
307
|
+
alternatedParams.direction = "reverse"
|
|
308
|
+
alternated = {
|
|
309
|
+
subtype: _animate.utils.nodes.Animation,
|
|
310
|
+
children: _animate.getTargetInterpolators(nodeTarget, alternatedParams, _animate.utils.shallowCopy(finalInterpolatorParams), finalAnimationParams.duration, customEaseFunction)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
animation.append(finalAnimationParams)
|
|
314
|
+
animation.repeat = false
|
|
315
|
+
|
|
316
|
+
alternated.append(finalAnimationParams)
|
|
317
|
+
alternated.repeat = false
|
|
318
|
+
|
|
319
|
+
seqAnim = {
|
|
320
|
+
subtype: _animate.utils.nodes.SequentialAnimation,
|
|
321
|
+
children: [_animate.utils.shallowCopy(animation), alternated]
|
|
322
|
+
}
|
|
323
|
+
seqAnim.repeat = finalAnimationParams.repeat
|
|
324
|
+
animations.push(seqAnim)
|
|
325
|
+
else
|
|
326
|
+
animation.append(finalAnimationParams)
|
|
327
|
+
animations.push(_animate.utils.shallowCopy(animation))
|
|
328
|
+
end if
|
|
329
|
+
end if
|
|
330
|
+
end for
|
|
331
|
+
|
|
332
|
+
if (recursive and subAnimations.count() > 0)
|
|
333
|
+
animations.append(subAnimations)
|
|
334
|
+
end if
|
|
335
|
+
|
|
336
|
+
if (animations.count() > 1)
|
|
337
|
+
multipleAnimations = {
|
|
338
|
+
subtype: _animate.utils.nodes.ParallelAnimation,
|
|
339
|
+
children: animations
|
|
340
|
+
}
|
|
341
|
+
if (recursive)
|
|
342
|
+
multipleAnimations.control = autoplay ? "start" : "none"
|
|
343
|
+
end if
|
|
344
|
+
return multipleAnimations
|
|
345
|
+
else if (animations.count() = 1)
|
|
346
|
+
if (recursive)
|
|
347
|
+
animations[0].control = autoplay ? "start" : "none"
|
|
348
|
+
end if
|
|
349
|
+
return animations[0]
|
|
350
|
+
end if
|
|
351
|
+
|
|
352
|
+
return animations
|
|
353
|
+
end function
|
|
354
|
+
|
|
355
|
+
function getTargetInterpolators(nodeTarget as object, params as object, globalInterpolatorParams as object, duration as float, customEaseFunction as dynamic) as object
|
|
356
|
+
interpolators = []
|
|
357
|
+
direction = params.direction
|
|
358
|
+
if (direction = invalid)
|
|
359
|
+
direction = "normal"
|
|
360
|
+
end if
|
|
361
|
+
params.delete("direction")
|
|
362
|
+
|
|
363
|
+
for each item in params.items()
|
|
364
|
+
' Thanks Roku for not letting us split if statements
|
|
365
|
+
' in multiple lines. This is very readable! :)
|
|
366
|
+
if (_animate.is.valid(item.value) and (_animate.is.special.prop(item.key) or nodeTarget.hasField(item.key)))
|
|
367
|
+
interpolator = _animate.getPropertyInterpolator(nodeTarget, item.key, item.value, duration, customEaseFunction)
|
|
368
|
+
if (interpolator <> invalid)
|
|
369
|
+
finalInterpolator = globalInterpolatorParams
|
|
370
|
+
finalInterpolator.append(interpolator)
|
|
371
|
+
|
|
372
|
+
if (direction = "reverse")
|
|
373
|
+
finalInterpolator.keyValue.reverse()
|
|
374
|
+
end if
|
|
375
|
+
|
|
376
|
+
interpolators.push(_animate.utils.shallowCopy(finalInterpolator))
|
|
377
|
+
end if
|
|
378
|
+
end if
|
|
379
|
+
end for
|
|
380
|
+
return interpolators
|
|
381
|
+
end function
|
|
382
|
+
|
|
383
|
+
function getPropertyInterpolator(nodeTarget as object, property as string, value as dynamic, duration, customEaseFunction as dynamic) as dynamic
|
|
384
|
+
if (property = "translation" and _animate.is.arr2d(value))
|
|
385
|
+
value = [
|
|
386
|
+
nodeTarget.translation[0] + cdbl(value[0]),
|
|
387
|
+
nodeTarget.translation[1] + cdbl(value[1])
|
|
388
|
+
]
|
|
389
|
+
else if (_animate.is.special.translation(property) and _animate.utils.compareFieldStrings(property, "translationTo") and _animate.is.arr2d(value))
|
|
390
|
+
value = [
|
|
391
|
+
cdbl(value[0]),
|
|
392
|
+
cdbl(value[1])
|
|
393
|
+
]
|
|
394
|
+
property = "translation"
|
|
395
|
+
else if (_animate.is.special.translation(property))
|
|
396
|
+
value = cdbl(value)
|
|
397
|
+
if (_animate.utils.compareFieldStrings(property, "x"))
|
|
398
|
+
value = [value, nodeTarget.translation[1]]
|
|
399
|
+
else if (_animate.utils.compareFieldStrings(property, "y"))
|
|
400
|
+
value = [nodeTarget.translation[0], value]
|
|
401
|
+
else if (_animate.utils.compareFieldStrings(property, "translateX"))
|
|
402
|
+
currentX = nodeTarget.translation[0]
|
|
403
|
+
value = [currentX + value, nodeTarget.translation[1]]
|
|
404
|
+
else if (_animate.utils.compareFieldStrings(property, "translateY"))
|
|
405
|
+
currentY = nodeTarget.translation[1]
|
|
406
|
+
value = [nodeTarget.translation[0], currentY + value]
|
|
407
|
+
end if
|
|
408
|
+
property = "translation"
|
|
409
|
+
end if
|
|
410
|
+
|
|
411
|
+
ops = GetInterface(value, "ifStringOps")
|
|
412
|
+
|
|
413
|
+
if (property = "rotation" and _animate.is.str(value) and ops <> invalid and ops.endsWith("deg"))
|
|
414
|
+
degrees = value.tokenize("deg")[0].toFloat()
|
|
415
|
+
value = degrees * _animate.utils.constants.radiansFactor
|
|
416
|
+
end if
|
|
417
|
+
|
|
418
|
+
if (property = "scale" and _animate.is.number(value))
|
|
419
|
+
if (_animate.is.integer(value))
|
|
420
|
+
value = cdbl(value)
|
|
421
|
+
end if
|
|
422
|
+
value = [value, value]
|
|
423
|
+
end if
|
|
424
|
+
|
|
425
|
+
currentValue = nodeTarget[property]
|
|
426
|
+
|
|
427
|
+
interpolator = {}
|
|
428
|
+
if (_animate.is.colorField(property))
|
|
429
|
+
interpolator.subtype = _animate.utils.nodes.ColorFieldInterpolator
|
|
430
|
+
else if (_animate.is.arr2d(value))
|
|
431
|
+
interpolator.subtype = _animate.utils.nodes.Vector2DFieldInterpolator
|
|
432
|
+
else if (_animate.is.number(value))
|
|
433
|
+
interpolator.subtype = _animate.utils.nodes.FloatFieldInterpolator
|
|
434
|
+
else
|
|
435
|
+
return invalid
|
|
436
|
+
end if
|
|
437
|
+
|
|
438
|
+
keyValue = [currentValue, value]
|
|
439
|
+
|
|
440
|
+
if (not _animate.is.colorField(property) and customEaseFunction <> invalid)
|
|
441
|
+
keyValue = _animate.utils.generateKeyValues(currentValue, value, 60 * duration, customEaseFunction)
|
|
442
|
+
end if
|
|
443
|
+
|
|
444
|
+
keyValueCount = keyValue.count()
|
|
445
|
+
key = []
|
|
446
|
+
_step = 1 / (keyValueCount - 1)
|
|
447
|
+
for i = 0 to keyValueCount - 1
|
|
448
|
+
key.push(i * _step)
|
|
449
|
+
end for
|
|
450
|
+
|
|
451
|
+
interpolator.append({
|
|
452
|
+
fieldToInterp: `${nodeTarget.id}.${property}`,
|
|
453
|
+
keyValue: keyValue,
|
|
454
|
+
key: key
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
return interpolator
|
|
458
|
+
end function
|
|
459
|
+
|
|
460
|
+
function getNodeTargets(targets as dynamic) as object
|
|
461
|
+
nodeTargets = []
|
|
462
|
+
|
|
463
|
+
if (_animate.is.str(targets))
|
|
464
|
+
' For a string `targets`, we split by spaces
|
|
465
|
+
' and try to find the nodes in the current `m.top` scope.
|
|
466
|
+
ids = targets.tokenize(" ")
|
|
467
|
+
for each id in ids
|
|
468
|
+
nodeTarget = m.top.findNode(id)
|
|
469
|
+
if (_animate.is.valid(nodeTarget))
|
|
470
|
+
nodeTargets.push(nodeTarget)
|
|
471
|
+
end if
|
|
472
|
+
end for
|
|
473
|
+
else if (_animate.is.arr(targets))
|
|
474
|
+
' For array targets, we recursively find targets inside
|
|
475
|
+
' each element.
|
|
476
|
+
for each target in targets
|
|
477
|
+
nodeTargets.append(getNodeTargets(target))
|
|
478
|
+
end for
|
|
479
|
+
else if (_animate.is._node(targets))
|
|
480
|
+
' For nodes we consider them directly
|
|
481
|
+
nodeTargets.push(targets)
|
|
482
|
+
end if
|
|
483
|
+
|
|
484
|
+
return nodeTargets
|
|
485
|
+
end function
|
|
486
|
+
|
|
487
|
+
class Timeline
|
|
488
|
+
private nodeObject as object = {}
|
|
489
|
+
private inheritableParams as object = {}
|
|
490
|
+
|
|
491
|
+
sub new(params as object)
|
|
492
|
+
sequential = false
|
|
493
|
+
if (params.sequential <> invalid and _animate.is.bool(params.sequential))
|
|
494
|
+
sequential = params.sequential
|
|
495
|
+
params.delete("sequential")
|
|
496
|
+
end if
|
|
497
|
+
|
|
498
|
+
animationAndInterpolatorParams = _animate.utils.extractAnimationParams(params)
|
|
499
|
+
animationAndInterpolatorParams.append(_animate.utils.extractInterpolatorParams(params))
|
|
500
|
+
|
|
501
|
+
m.inheritableParams = _animate.utils.extractParams(params, [
|
|
502
|
+
"targets",
|
|
503
|
+
"target",
|
|
504
|
+
"easeFunction",
|
|
505
|
+
"duration",
|
|
506
|
+
"delay"
|
|
507
|
+
])
|
|
508
|
+
|
|
509
|
+
nodeName = _animate.utils.nodes.ParallelAnimation
|
|
510
|
+
if (sequential)
|
|
511
|
+
nodeName = _animate.utils.nodes.SequentialAnimation
|
|
512
|
+
end if
|
|
513
|
+
|
|
514
|
+
m.nodeObject = {
|
|
515
|
+
subtype: nodeName,
|
|
516
|
+
children: []
|
|
517
|
+
}
|
|
518
|
+
m.nodeObject.append(animationAndInterpolatorParams)
|
|
519
|
+
end sub
|
|
520
|
+
|
|
521
|
+
function add(params as object) as object
|
|
522
|
+
if (params.target <> invalid)
|
|
523
|
+
params.targets = params.target
|
|
524
|
+
params.delete("target")
|
|
525
|
+
end if
|
|
526
|
+
|
|
527
|
+
finalParams = _animate.utils.shallowCopy(m.inheritableParams)
|
|
528
|
+
finalParams.append(params)
|
|
529
|
+
m.nodeObject.children.push(_animate.create(finalParams))
|
|
530
|
+
return m
|
|
531
|
+
end function
|
|
532
|
+
|
|
533
|
+
function getAnimation() as object
|
|
534
|
+
return _animate.utils.createNodeFromAA(m.nodeObject)
|
|
535
|
+
end function
|
|
536
|
+
end class
|
|
537
|
+
|
|
538
|
+
namespace is
|
|
539
|
+
function valid(x as dynamic) as boolean
|
|
540
|
+
return x <> invalid
|
|
541
|
+
end function
|
|
542
|
+
|
|
543
|
+
function bool(x as dynamic) as boolean
|
|
544
|
+
return _animate.is.valid(x) and getInterface(x, "ifBoolean") <> invalid
|
|
545
|
+
end function
|
|
546
|
+
|
|
547
|
+
function str(x as dynamic) as boolean
|
|
548
|
+
return _animate.is.valid(x) and getInterface(x, "ifString") <> invalid
|
|
549
|
+
end function
|
|
550
|
+
|
|
551
|
+
function arr(x as dynamic) as boolean
|
|
552
|
+
return _animate.is.valid(x) and getInterface(x, "ifArray") <> invalid
|
|
553
|
+
end function
|
|
554
|
+
|
|
555
|
+
function integer(x as dynamic) as boolean
|
|
556
|
+
return _animate.is.valid(x) and getInterface(x, "ifInt") <> invalid and (type(x) = "roInt" or type(x) = "roInteger" or type(x) = "Integer")
|
|
557
|
+
end function
|
|
558
|
+
|
|
559
|
+
function float(x as dynamic) as boolean
|
|
560
|
+
return _animate.is.valid(x) and (getInterface(x, "ifFloat") <> invalid or (type(x) = "roFloat" or type(x) = "Float"))
|
|
561
|
+
end function
|
|
562
|
+
|
|
563
|
+
function number(x as dynamic) as boolean
|
|
564
|
+
return _animate.is.float(x) or _animate.is.integer(x)
|
|
565
|
+
end function
|
|
566
|
+
|
|
567
|
+
function _node(x as dynamic) as boolean
|
|
568
|
+
return _animate.is.valid(x) and getInterface(x, "ifSGNodeField") <> invalid
|
|
569
|
+
end function
|
|
570
|
+
|
|
571
|
+
function arr2d(x as dynamic) as boolean
|
|
572
|
+
return _animate.is.valid(x) and _animate.is.arr(x) and x.count() = 2 and _animate.is.number(x[0]) and _animate.is.number(x[1])
|
|
573
|
+
end function
|
|
574
|
+
|
|
575
|
+
function _aa(x as dynamic) as boolean
|
|
576
|
+
return _animate.is.valid(x) and getInterface(x, "ifAssociativeArray") <> invalid
|
|
577
|
+
end function
|
|
578
|
+
|
|
579
|
+
function func(x as dynamic) as boolean
|
|
580
|
+
return _animate.is.valid(x) and getInterface(x, "ifFunction") <> invalid
|
|
581
|
+
end function
|
|
582
|
+
|
|
583
|
+
function colorField(x as dynamic) as boolean
|
|
584
|
+
colorFields = ["color", "blendColor"]
|
|
585
|
+
if (not _animate.is.str(x))
|
|
586
|
+
return false
|
|
587
|
+
end if
|
|
588
|
+
for each cf in colorFields
|
|
589
|
+
if (_animate.utils.compareFieldStrings(cf, x))
|
|
590
|
+
return true
|
|
591
|
+
end if
|
|
592
|
+
end for
|
|
593
|
+
return false
|
|
594
|
+
end function
|
|
595
|
+
|
|
596
|
+
namespace special
|
|
597
|
+
function prop(x as dynamic) as boolean
|
|
598
|
+
return _animate.is.special.translation(x)
|
|
599
|
+
end function
|
|
600
|
+
|
|
601
|
+
function translation(x as dynamic) as boolean
|
|
602
|
+
if (not _animate.is.str(x))
|
|
603
|
+
return false
|
|
604
|
+
end if
|
|
605
|
+
|
|
606
|
+
specialProperties = ["x", "y", "translateX", "translateY", "translationTo"]
|
|
607
|
+
for each specialProperty in specialProperties
|
|
608
|
+
if (_animate.utils.compareFieldStrings(specialProperty, x))
|
|
609
|
+
return true
|
|
610
|
+
end if
|
|
611
|
+
end for
|
|
612
|
+
|
|
613
|
+
return false
|
|
614
|
+
end function
|
|
615
|
+
end namespace
|
|
616
|
+
end namespace
|
|
617
|
+
|
|
618
|
+
namespace utils
|
|
619
|
+
|
|
620
|
+
function createNodeFromAA(aa as object) as object
|
|
621
|
+
' Make sure we have an associative array to process
|
|
622
|
+
if Type(aa) <> "roAssociativeArray"
|
|
623
|
+
return invalid
|
|
624
|
+
end if
|
|
625
|
+
|
|
626
|
+
' Fetch subtype if it exists, otherwise provide a default
|
|
627
|
+
nodeSubtype = "Group" ' Example default node type
|
|
628
|
+
if aa.Lookup("subtype") <> invalid
|
|
629
|
+
nodeSubtype = aa.subtype
|
|
630
|
+
end if
|
|
631
|
+
|
|
632
|
+
' Create a new node with the given (or default) subtype
|
|
633
|
+
node = CreateObject("roSGNode", nodeSubtype)
|
|
634
|
+
|
|
635
|
+
' If we have a "children" key and it's an array, recurse
|
|
636
|
+
if aa.Lookup("children") <> invalid and Type(aa.children) = "roArray"
|
|
637
|
+
for each childAA in aa.children
|
|
638
|
+
' Recursively create child nodes
|
|
639
|
+
childNode = _animate.utils.createNodeFromAA(childAA)
|
|
640
|
+
if childNode <> invalid
|
|
641
|
+
node.appendChild(childNode)
|
|
642
|
+
end if
|
|
643
|
+
end for
|
|
644
|
+
end if
|
|
645
|
+
' Clean up the keys we no longer need in this AA
|
|
646
|
+
aa.delete("subtype")
|
|
647
|
+
aa.delete("children")
|
|
648
|
+
|
|
649
|
+
' Set all remaining fields on the node from the AA
|
|
650
|
+
node.update(aa, true)
|
|
651
|
+
|
|
652
|
+
return node
|
|
653
|
+
end function
|
|
654
|
+
|
|
655
|
+
function compareFieldStrings(x as string, y as string) as boolean
|
|
656
|
+
return lcase(x) = lcase(y)
|
|
657
|
+
end function
|
|
658
|
+
|
|
659
|
+
function getAnimationParams() as object
|
|
660
|
+
return [
|
|
661
|
+
"repeat",
|
|
662
|
+
"delay",
|
|
663
|
+
"duration",
|
|
664
|
+
"easeFunction",
|
|
665
|
+
"easeInPercent",
|
|
666
|
+
"easeOutPercent",
|
|
667
|
+
"optional"
|
|
668
|
+
]
|
|
669
|
+
end function
|
|
670
|
+
|
|
671
|
+
function getInterpolatorParams() as object
|
|
672
|
+
return [
|
|
673
|
+
"fraction",
|
|
674
|
+
"reverse"
|
|
675
|
+
]
|
|
676
|
+
end function
|
|
677
|
+
|
|
678
|
+
function extractParams(source as object, wantedParams as object) as object
|
|
679
|
+
result = {}
|
|
680
|
+
for each wantedParam in wantedParams
|
|
681
|
+
if (source[wantedParam] <> invalid)
|
|
682
|
+
result[wantedParam] = source[wantedParam]
|
|
683
|
+
end if
|
|
684
|
+
end for
|
|
685
|
+
return result
|
|
686
|
+
end function
|
|
687
|
+
|
|
688
|
+
function extractAnimationParams(params as object) as object
|
|
689
|
+
return _animate.utils.extractParams(params, _animate.utils.getAnimationParams())
|
|
690
|
+
end function
|
|
691
|
+
|
|
692
|
+
function extractInterpolatorParams(params as object) as object
|
|
693
|
+
return _animate.utils.extractParams(params, _animate.utils.getInterpolatorParams())
|
|
694
|
+
end function
|
|
695
|
+
|
|
696
|
+
function shallowCopy(x as object) as object
|
|
697
|
+
y = {}
|
|
698
|
+
if (_animate.is.arr(x))
|
|
699
|
+
y = []
|
|
700
|
+
end if
|
|
701
|
+
y.append(x)
|
|
702
|
+
return y
|
|
703
|
+
end function
|
|
704
|
+
|
|
705
|
+
function getFunctionValueParams(params as object, targetInfo as object) as object
|
|
706
|
+
copy = _animate.utils.shallowCopy(params)
|
|
707
|
+
for each item in copy.items()
|
|
708
|
+
if (_animate.is.func(item.value))
|
|
709
|
+
copy[item.key] = item.value(targetInfo.nodeTarget, targetInfo.index, targetInfo.targetsLength)
|
|
710
|
+
end if
|
|
711
|
+
end for
|
|
712
|
+
return copy
|
|
713
|
+
end function
|
|
714
|
+
|
|
715
|
+
function generateKeyValues(_start as dynamic, _end as dynamic, frames as float, easeFunc as function)
|
|
716
|
+
keyFrames = []
|
|
717
|
+
for frame = 0 to cint(frames) - 1
|
|
718
|
+
' Normalize the current frame number to the range [0, 1]
|
|
719
|
+
normalizedFrame = frame / (frames - 1)
|
|
720
|
+
' Apply the easing function
|
|
721
|
+
easedFrame = easeFunc(normalizedFrame)
|
|
722
|
+
|
|
723
|
+
if (_animate.is.arr2d(_start) and _animate.is.arr2d(_end))
|
|
724
|
+
' Map the result to the range [start, end] for each component
|
|
725
|
+
d1 = _start[0] + (_end[0] - _start[0]) * easedFrame
|
|
726
|
+
d2 = _start[1] + (_end[1] - _start[1]) * easedFrame
|
|
727
|
+
value = [d1, d2]
|
|
728
|
+
else
|
|
729
|
+
' Map the result to the range [start, end]
|
|
730
|
+
value = _start + (_end - _start) * easedFrame
|
|
731
|
+
end if
|
|
732
|
+
|
|
733
|
+
' Add the calculated value to the list of keyframes
|
|
734
|
+
keyFrames.push(value)
|
|
735
|
+
end for
|
|
736
|
+
return keyFrames
|
|
737
|
+
end function
|
|
738
|
+
|
|
739
|
+
enum nodes
|
|
740
|
+
Animation = "Animation"
|
|
741
|
+
ParallelAnimation = "ParallelAnimation"
|
|
742
|
+
SequentialAnimation = "SequentialAnimation"
|
|
743
|
+
ColorFieldInterpolator = "ColorFieldInterpolator"
|
|
744
|
+
Vector2DFieldInterpolator = "Vector2DFieldInterpolator"
|
|
745
|
+
FloatFieldInterpolator = "FloatFieldInterpolator"
|
|
746
|
+
end enum
|
|
747
|
+
|
|
748
|
+
enum constants
|
|
749
|
+
pi = 3.14159265
|
|
750
|
+
radiansFactor = 0.01745329
|
|
751
|
+
end enum
|
|
752
|
+
end namespace
|
|
753
|
+
end namespace
|