@ue-too/animate 0.5.2 → 0.6.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 (28) hide show
  1. package/package.json +9 -10
  2. package/.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/animatable-attribute.js +0 -112
  3. package/.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/animatable-attribute.js.map +0 -1
  4. package/.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/composite-animation.js +0 -792
  5. package/.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/composite-animation.js.map +0 -1
  6. package/.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/index.js +0 -3
  7. package/.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/index.js.map +0 -1
  8. package/dist/animatable-attribute.d.ts +0 -39
  9. package/dist/animate.tsbuildinfo +0 -1
  10. package/dist/composite-animation.d.ts +0 -181
  11. package/dist/index.d.ts +0 -2
  12. package/dist/package.json +0 -22
  13. package/jest.config.js +0 -18
  14. package/project.json +0 -33
  15. package/rollup.config.js +0 -20
  16. package/src/animatable-attribute.ts +0 -146
  17. package/src/composite-animation.ts +0 -950
  18. package/src/index.ts +0 -2
  19. package/test/animatable-attributes.test.ts +0 -156
  20. package/test/composite-animation.test.ts +0 -238
  21. package/tsconfig.json +0 -22
  22. package/tsconfig.spec.json +0 -12
  23. /package/{.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/animatable-attribute.d.ts → animatable-attribute.d.ts} +0 -0
  24. /package/{.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/animate.tsbuildinfo → animate.tsbuildinfo} +0 -0
  25. /package/{.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/composite-animation.d.ts → composite-animation.d.ts} +0 -0
  26. /package/{.rollup.cache/home/runner/work/ue-too/ue-too/packages/animate/dist/index.d.ts → index.d.ts} +0 -0
  27. /package/{dist/index.js → index.js} +0 -0
  28. /package/{dist/index.js.map → index.js.map} +0 -0
@@ -1,950 +0,0 @@
1
- import { AnimatableAttributeHelper, Keyframe } from "./animatable-attribute";
2
-
3
-
4
- export const linear = (percentage: number) => {
5
- return percentage;
6
- }
7
-
8
- export interface Animator{
9
- loops: boolean;
10
- duration: number;
11
- delay: number;
12
- drag: number;
13
- nonCascadingDuration(newDuration: number): void;
14
- start(): void;
15
- stop(): void;
16
- pause(): void;
17
- resume(): void;
18
- animate(deltaTime: number): void;
19
- setUp(): void;
20
- resetAnimationState(): void;
21
- tearDown(): void;
22
- setParent(parent: AnimatorContainer): void;
23
- detachParent(): void;
24
- toggleReverse(reverse: boolean): void;
25
- onEnd(callback: Function): UnSubscribe;
26
- onStart(callback: Function): UnSubscribe;
27
- clearOnStart(): void;
28
- clearOnEnd(): void;
29
- maxLoopCount: number | undefined;
30
- playing: boolean;
31
- }
32
-
33
- export type UnSubscribe = () => void;
34
-
35
- export interface AnimatorContainer {
36
- updateDuration(): void;
37
- checkCyclicChildren(): boolean;
38
- containsAnimation(animationInInterest: Animator): boolean;
39
- }
40
-
41
- export class CompositeAnimation implements Animator, AnimatorContainer{
42
-
43
- private animations: Map<string, {animator: Animator, startTime?: number}>;
44
- private localTime: number;
45
- private _duration: number;
46
- private onGoing: boolean;
47
- private loop: boolean;
48
- private playedTime: number;
49
- private setUpFn: Function;
50
- private tearDownFn: Function;
51
- private _dragTime: number;
52
- private _delayTime: number;
53
- private parent: AnimatorContainer | undefined;
54
- private _maxLoopCount: number | undefined;
55
-
56
- private endCallbacks: Function[] = [];
57
- private startCallbacks: Function[] = [];
58
-
59
- private reverse: boolean;
60
-
61
- constructor(animations: Map<string, {animator: Animator, startTime?: number}> = new Map(), loop: boolean = false, parent: AnimatorContainer | undefined = undefined, setupFn: Function = ()=>{}, tearDownFn: Function = ()=>{}){
62
- this.animations = animations;
63
- this._duration = 0;
64
- this.calculateDuration();
65
- this.localTime = -1;
66
- this.onGoing = false;
67
- this.loop = loop;
68
- this.setUpFn = setupFn;
69
- this.tearDownFn = tearDownFn;
70
- this._delayTime = 0;
71
- this._dragTime = 0;
72
- this.parent = parent;
73
- this.animations.forEach((animation) => {
74
- animation.animator.setParent(this);
75
- });
76
- this.reverse = false;
77
- this.playedTime = 0;
78
- }
79
-
80
- toggleReverse(reverse: boolean){
81
- if(this.reverse == reverse){
82
- return;
83
- }
84
- this.reverse = reverse;
85
- this.animations.forEach((animation) => {
86
- animation.animator.toggleReverse(reverse);
87
- });
88
- }
89
-
90
- setParent(parent: AnimatorContainer){
91
- this.parent = parent;
92
- }
93
-
94
- detachParent(){
95
- this.parent = undefined;
96
- }
97
-
98
- animate(deltaTime: number): void {
99
- if(!this.onGoing || this.localTime > this._duration + this._delayTime + this._dragTime || this.localTime < 0 || this.animations.size == 0){
100
- return;
101
- }
102
- this.localTime += deltaTime;
103
- if (this.localTime - deltaTime <= 0 && deltaTime > 0){
104
- // console.log("composite animation start");
105
- this.startCallbacks.forEach((callback) => {
106
- queueMicrotask(()=>{callback()});
107
- });
108
- }
109
- this.animateChildren(deltaTime);
110
- this.checkTerminalAndLoop();
111
- }
112
-
113
- checkTerminalAndLoop(){
114
- if(this.localTime >= this._duration + this._delayTime + this._dragTime){
115
- // console.log("composite animation end");
116
- this.playedTime += 1;
117
- this.endCallbacks.forEach((callback) => {
118
- queueMicrotask(()=>{callback()});
119
- });
120
- if(!this.loops || (this.maxLoopCount != undefined && this.playedTime >= this.maxLoopCount)){
121
- // this.onGoing = false;
122
- this.stop();
123
- } else {
124
- // if loop is true and current loop is not the last loop, then prepare to start the animations again
125
- // this.onGoing = true;
126
- // this.localTime = 0;
127
- // this.animations.forEach((animation) => {
128
- // if(animation.animator.loops){
129
- // animation.animator.startAnimation();
130
- // }
131
- // });
132
- this.start();
133
- }
134
- }
135
- }
136
-
137
- animateChildren(deltaTime: number){
138
- const prevLocalTime = this.localTime - deltaTime;
139
- if(this.localTime < this._delayTime){
140
- return;
141
- }
142
- this.animations.forEach((animation, name: string) => {
143
- if(animation.startTime == undefined){
144
- animation.startTime = 0;
145
- }
146
- if(!this.childShouldAnimate(animation, prevLocalTime)){
147
- this.wrapUpAnimator({animator: animation.animator, startTime: animation.startTime, name: name}, prevLocalTime);
148
- return;
149
- }
150
- if(prevLocalTime - this._delayTime < animation.startTime){
151
- animation.animator.animate(this.localTime - this._delayTime - animation.startTime);
152
- } else {
153
- animation.animator.animate(deltaTime);
154
- }
155
- });
156
- }
157
-
158
- childShouldAnimate(animation: {animator: Animator, startTime?: number}, prevLocalTime: number): boolean{
159
- if(animation.startTime == undefined){
160
- animation.startTime = 0;
161
- }
162
- if(this.localTime - this._delayTime >= animation.startTime && this.localTime - this._delayTime <= animation.startTime + animation.animator.duration){
163
- return true;
164
- }
165
- return false;
166
- }
167
-
168
- wrapUpAnimator(animation: {animator: Animator, startTime?: number, name: string}, prevLocalTime: number){
169
- if(animation.startTime == undefined){
170
- animation.startTime = 0;
171
- }
172
- if(this.localTime - this._delayTime > animation.startTime + animation.animator.duration && prevLocalTime - this._delayTime < animation.startTime + animation.animator.duration){
173
- // console.log("wrap up", animation.name);
174
- // console.log("time remaining", animation.startTime + animation.animator.duration - (prevLocalTime - this._delayTime));
175
-
176
- animation.animator.animate(animation.startTime + animation.animator.duration - (prevLocalTime - this._delayTime));
177
- }
178
- }
179
-
180
- pause(): void {
181
- this.onGoing = false;
182
- this.animations.forEach((animation) => {
183
- animation.animator.pause();
184
- });
185
- }
186
-
187
- resume(): void {
188
- this.onGoing = true;
189
- this.animations.forEach((animation) => {
190
- animation.animator.resume();
191
- });
192
- }
193
-
194
- start(): void {
195
- this.onGoing = true;
196
- this.setUp();
197
- this.localTime = 0;
198
- this.animations.forEach((animation) => {
199
- animation.animator.start();
200
- });
201
- }
202
-
203
- stop(): void {
204
- this.onGoing = false;
205
- this.playedTime = 0;
206
- this.localTime = this._duration + 0.1;
207
- this.animations.forEach((animation) => {
208
- animation.animator.stop();
209
- });
210
- this.tearDown();
211
- }
212
-
213
- get duration(): number {
214
- return this._duration + this._delayTime + this._dragTime;
215
- }
216
-
217
- set duration(duration: number) {
218
- if(duration < 0){
219
- return;
220
- }
221
- const originalDuration = this._duration + this._delayTime + this._dragTime;
222
- const scale = duration / originalDuration;
223
- const newDelayTime = this._delayTime * scale;
224
- const newDragTime = this._dragTime * scale;
225
- this._delayTime = newDelayTime;
226
- this._dragTime = newDragTime;
227
- this.animations.forEach((animation)=>{
228
- if(animation.startTime == undefined){
229
- animation.startTime = 0;
230
- }
231
- animation.startTime *= scale;
232
- const newDuration = animation.animator.duration * scale;
233
- animation.animator.nonCascadingDuration(newDuration);
234
- });
235
- this.calculateDuration();
236
- if(this.parent != undefined){
237
- this.parent.updateDuration();
238
- }
239
- }
240
-
241
- nonCascadingDuration(newDuration: number): void {
242
- if(newDuration < 0){
243
- return;
244
- }
245
- const originalDuration = this._duration + this._delayTime + this._dragTime;
246
- const scale = newDuration / originalDuration;
247
- const newDelayTime = this._delayTime * scale;
248
- const newDragTime = this._dragTime * scale;
249
- this._delayTime = newDelayTime;
250
- this._dragTime = newDragTime;
251
- this.animations.forEach((animation)=>{
252
- if(animation.startTime == undefined){
253
- animation.startTime = 0;
254
- }
255
- animation.startTime *= scale;
256
- const newDuration = animation.animator.duration * scale;
257
- animation.animator.nonCascadingDuration(newDuration);
258
- });
259
- this.calculateDuration();
260
- }
261
-
262
- resetAnimationState(): void {
263
- this.onGoing = false;
264
- this.animations.forEach((animation) => {
265
- animation.animator.resetAnimationState();
266
- });
267
- }
268
-
269
- getTrueDuration(): number{
270
- return this._duration;
271
- }
272
-
273
- setUp(): void {
274
- this.setUpFn();
275
- this.animations.forEach((animation) => {
276
- animation.animator.setUp();
277
- });
278
- }
279
-
280
- tearDown(): void {
281
- this.tearDownFn();
282
- this.animations.forEach((animation) => {
283
- animation.animator.tearDown();
284
- });
285
- }
286
-
287
- addAnimation(name: string, animation: Animator, startTime: number = 0, endCallback: Function = ()=>{}){
288
- if(this.animations.has(name)){
289
- return;
290
- }
291
- if(this.parent !== undefined && this.parent.containsAnimation(animation)){
292
- return;
293
- }
294
- this.animations.set(name, {animator: animation, startTime: startTime});
295
- animation.setParent(this);
296
- if(this.localTime > startTime){
297
- animation.animate(this.localTime - startTime);
298
- }
299
- const endTime = startTime + animation.duration;
300
- this._duration = Math.max(this._duration, endTime);
301
- if(this.parent != undefined){
302
- this.parent.updateDuration();
303
- }
304
- }
305
-
306
- addAnimationAfter(name: string, animation: Animator, afterName: string, delay: number = 0){
307
- let afterAnimation = this.animations.get(afterName);
308
- if(afterAnimation == undefined){
309
- return;
310
- }
311
- if(afterAnimation.startTime == undefined){
312
- afterAnimation.startTime = 0;
313
- }
314
- let startTime = afterAnimation.startTime + afterAnimation.animator.duration;
315
- startTime += delay;
316
- this.addAnimation(name, animation, startTime);
317
- this.calculateDuration();
318
- if(this.parent != undefined){
319
- this.parent.updateDuration();
320
- }
321
- }
322
-
323
- addAnimationAdmist(name: string, animation: Animator, admistName: string, delay: number){
324
- let admistAnimation = this.animations.get(admistName);
325
- if(admistAnimation == undefined){
326
- return;
327
- }
328
- if(admistAnimation.startTime == undefined){
329
- admistAnimation.startTime = 0;
330
- }
331
- let startTime = admistAnimation.startTime + delay;
332
- this.addAnimation(name, animation, startTime);
333
- this.calculateDuration();
334
- if(this.parent != undefined){
335
- this.parent.updateDuration();
336
- }
337
- }
338
-
339
- addAnimationBefore(name: string, animation: Animator, beforeName: string, aheadTime: number = 0){
340
- let beforeAnimation = this.animations.get(beforeName);
341
- if(beforeAnimation == undefined){
342
- return;
343
- }
344
- if(beforeAnimation.startTime == undefined){
345
- beforeAnimation.startTime = 0;
346
- }
347
- let startTime = beforeAnimation.startTime;
348
- startTime -= aheadTime;
349
- this.addAnimation(name, animation, startTime);
350
- if (startTime < 0){
351
- const pushOver = 0 - startTime;
352
- this.animations.forEach((animation) => {
353
- if(animation.startTime == undefined){
354
- animation.startTime = 0;
355
- }
356
- animation.startTime += pushOver;
357
- });
358
- }
359
- this.calculateDuration();
360
- if(this.parent != undefined){
361
- this.parent.updateDuration();
362
- }
363
- }
364
-
365
- removeAnimation(name: string){
366
- let animation = this.animations.get(name);
367
- let deleted = this.animations.delete(name);
368
- if(deleted){
369
- if(animation != undefined){
370
- animation.animator.detachParent();
371
- }
372
- this.calculateDuration();
373
- if(this.parent != undefined){
374
- this.parent.updateDuration();
375
- }
376
- }
377
- }
378
-
379
- set delay(delayTime: number){
380
- this._delayTime = delayTime;
381
- if(this.parent != undefined){
382
- this.parent.updateDuration();
383
- }
384
- }
385
-
386
- get delay(): number{
387
- return this._delayTime;
388
- }
389
-
390
- set drag(dragTime: number){
391
- this._dragTime = dragTime;
392
- if(this.parent != undefined){
393
- this.parent.updateDuration();
394
- }
395
- }
396
-
397
- get drag(): number {
398
- return this._dragTime;
399
- }
400
-
401
- removeDelay(){
402
- this._delayTime = 0;
403
- if(this.parent != undefined){
404
- this.parent.updateDuration();
405
- }
406
- }
407
-
408
- removeDrag(){
409
- this._dragTime = 0;
410
- if(this.parent != undefined){
411
- this.parent.updateDuration();
412
- }
413
- }
414
-
415
- updateDuration(): void {
416
- if(this.checkCyclicChildren()){
417
- return;
418
- }
419
- this.calculateDuration();
420
- if(this.parent != undefined){
421
- this.parent.updateDuration();
422
- }
423
- }
424
-
425
- calculateDuration(){
426
- this._duration = 0;
427
- this.animations.forEach((animation)=>{
428
- if(animation.startTime == undefined){
429
- animation.startTime = 0;
430
- }
431
- const endTime = animation.startTime + animation.animator.duration;
432
- this._duration = Math.max(this._duration, endTime);
433
- });
434
- }
435
-
436
- get loops(): boolean {
437
- return this.loop;
438
- }
439
-
440
- set loops(loop: boolean) {
441
- this.loop = loop;
442
- }
443
-
444
- checkCyclicChildren(): boolean {
445
- const allChildren: Animator[] = [];
446
- allChildren.push(this);
447
- const visited = new Set<Animator>();
448
- while(allChildren.length > 0){
449
- const current = allChildren.pop();
450
- if(current == undefined){
451
- continue;
452
- }
453
- if(visited.has(current)){
454
- return true;
455
- }
456
- visited.add(current);
457
- if(current instanceof CompositeAnimation){
458
- current.animations.forEach((animation) => {
459
- allChildren.push(animation.animator);
460
- });
461
- }
462
- }
463
- return false;
464
- }
465
-
466
- forceToggleLoop(loop: boolean){
467
- this.loop = true;
468
- this.animations.forEach((animation) => {
469
- animation.animator.loops = true;
470
- });
471
- }
472
-
473
- containsAnimation(animationInInterest: Animator): boolean {
474
- if(this.parent !== undefined){
475
- return this.parent.containsAnimation(animationInInterest);
476
- }
477
- const allChildren: Animator[] = [];
478
- allChildren.push(this);
479
- const visited = new Set<Animator>();
480
- while(allChildren.length > 0){
481
- const current = allChildren.pop();
482
- if(current == undefined){
483
- continue;
484
- }
485
- if(current == animationInInterest){
486
- return true;
487
- }
488
- if(visited.has(current)){
489
- continue;
490
- }
491
- visited.add(current);
492
- if(current instanceof CompositeAnimation){
493
- current.animations.forEach((animation) => {
494
- allChildren.push(animation.animator);
495
- });
496
- }
497
- }
498
- return false;
499
- }
500
-
501
- onEnd(callback: Function): UnSubscribe{
502
- this.endCallbacks.push(callback);
503
- return ()=>{
504
- this.endCallbacks = this.endCallbacks.filter((cb) => cb != callback);
505
- }
506
- }
507
-
508
- onStart(callback: Function): UnSubscribe{
509
- this.startCallbacks.push(callback);
510
- return ()=>{
511
- this.startCallbacks = this.startCallbacks.filter((cb) => cb != callback);
512
- }
513
- }
514
-
515
- clearOnEnd(): void {
516
- this.endCallbacks = [];
517
- }
518
-
519
- clearOnStart(): void {
520
- this.startCallbacks = [];
521
- }
522
-
523
- get playing(): boolean {
524
- return this.onGoing;
525
- }
526
-
527
- get maxLoopCount(): number | undefined {
528
- return this._maxLoopCount;
529
- }
530
-
531
- set maxLoopCount(maxLoopCount: number | undefined) {
532
- this._maxLoopCount = maxLoopCount;
533
- }
534
- }
535
-
536
- export class Animation<T> implements Animator{
537
-
538
- private localTime: number; // local time starting from 0 up til duration
539
- private _duration: number;
540
- private keyframes: Keyframe<T>[];
541
- private animatableAttributeHelper: AnimatableAttributeHelper<T>;
542
- private applyAnimationValue: (value: T) => void;
543
- private easeFn: (percentage: number) => number;
544
- private onGoing: boolean;
545
- private currentKeyframeIndex: number;
546
- private loop: boolean;
547
- private playedTime: number;
548
- private setUpFn: Function;
549
- private tearDownFn: Function;
550
- private parent: AnimatorContainer | undefined;
551
- private delayTime: number = 0;
552
- private dragTime: number = 0;
553
-
554
- private reverse: boolean = false;
555
- private endCallbacks: Function[] = [];
556
- private startCallbacks: Function[] = [];
557
- private startAfterDelayCallbacks: Function[] = [];
558
-
559
- private zeroPercentageValue: T;
560
- private _maxLoopCount: number | undefined;
561
- private _fillMode: 'none' | 'forwards' | 'backwards' | 'both' = 'none';
562
-
563
- constructor(keyFrames: Keyframe<T>[], applyAnimationValue: (value: T) => void, animatableAttributeHelper: AnimatableAttributeHelper<T>, duration: number = 1000, loop: boolean = false, parent: AnimatorContainer | undefined = undefined, setUpFn: Function = ()=>{}, tearDownFn: Function = ()=>{}, easeFn: (percentage: number) => number = linear){
564
- this._duration = duration;
565
- this.keyframes = keyFrames;
566
- this.animatableAttributeHelper = animatableAttributeHelper;
567
- this.applyAnimationValue = applyAnimationValue;
568
- this.easeFn = easeFn;
569
- this.onGoing = false;
570
- this.localTime = duration + 0.1;
571
- this.currentKeyframeIndex = 0;
572
- this.loop = loop;
573
- this.setUpFn = setUpFn;
574
- this.tearDownFn = tearDownFn;
575
- this.parent = parent;
576
- this.playedTime = 0;
577
- this.zeroPercentageValue = this.findValue(0, keyFrames, animatableAttributeHelper);
578
- }
579
-
580
- toggleReverse(reverse: boolean): void{
581
- this.reverse = reverse;
582
- }
583
-
584
- start(): void{
585
- this.localTime = 0;
586
- this.currentKeyframeIndex = 0;
587
- this.onGoing = true;
588
- // this.applyAnimationValue(this.zeroPercentageValue);
589
- this.setUp();
590
- }
591
-
592
- stop(): void{
593
- this.onGoing = false;
594
- this.localTime = this._duration + this.dragTime + this.delayTime + 0.1;
595
- this.playedTime = 0;
596
- this.tearDown();
597
- }
598
-
599
- pause(): void{
600
- this.onGoing = false;
601
- }
602
-
603
- resume(){
604
- this.onGoing = true;
605
- }
606
-
607
- get playing(): boolean {
608
- return this.onGoing;
609
- }
610
-
611
- animate(deltaTime: number){
612
- if(this.onGoing != true || this.localTime < 0) {
613
- return;
614
- }
615
- if(deltaTime == 0){
616
- return;
617
- }
618
- this.localTime += deltaTime;
619
- // console.log("--------------------");
620
- // console.log("local time", this.localTime);
621
- // console.log("delta time", deltaTime);
622
- if(this.localTime - deltaTime <= 0 && deltaTime > 0){
623
- // console.log("--------------------");
624
- // console.log("current localtime", this.localTime);
625
- // console.log("current delta time", deltaTime);
626
- // console.log("previous local time", this.localTime - deltaTime);
627
- // console.log("animation start");
628
- // console.log(`the animation has been played ${this.playedTime} times`);
629
- // console.log(`the animation is now playing for the ${this.playedTime + 1} time`);
630
- this.startCallbacks.forEach((callback) => {
631
- queueMicrotask(()=>{callback()});
632
- });
633
- }
634
- if(this.localTime >= this.delayTime && (this.localTime <= this.delayTime + this._duration + this.dragTime || this.localTime - deltaTime <= this.delayTime + this._duration + this.dragTime)){
635
- // console.log("local time", this.localTime);
636
- // console.log("duration", this.duration);
637
- // console.log("local time would trigger end", this.localTime >= this._duration + this.delayTime + this.dragTime);
638
- // console.log("delta time", deltaTime);
639
- if(this.localTime - deltaTime <= this.delayTime && this.delayTime !== 0 && deltaTime > 0){
640
- this.startAfterDelayCallbacks.forEach((callback) => {
641
- queueMicrotask(()=>{callback()});
642
- });
643
- this.applyAnimationValue(this.zeroPercentageValue);
644
- }
645
- let localTimePercentage = (this.localTime - this.delayTime) / (this._duration);
646
- let targetPercentage = this.easeFn(localTimePercentage);
647
- if (localTimePercentage > 1){
648
- targetPercentage = this.easeFn(1);
649
- }
650
- let value: T;
651
- // console.log("currentKeyframeIndex", this.currentKeyframeIndex, "length", this.keyFrames.length);
652
- if(this.currentKeyframeIndex < this.keyframes.length && this.currentKeyframeIndex >= 0 && (this.reverse ? 1 - this.keyframes[this.currentKeyframeIndex].percentage == targetPercentage : this.keyframes[this.currentKeyframeIndex].percentage == targetPercentage) ){
653
- value = this.keyframes[this.currentKeyframeIndex].value;
654
- } else {
655
- value = this.findValue(targetPercentage, this.keyframes, this.animatableAttributeHelper);
656
- }
657
- if(this.reverse){
658
- while(this.currentKeyframeIndex >= 0 && 1 - this.keyframes[this.currentKeyframeIndex].percentage <= targetPercentage){
659
- this.currentKeyframeIndex -= 1;
660
- }
661
- } else {
662
- while(this.currentKeyframeIndex < this.keyframes.length && this.keyframes[this.currentKeyframeIndex].percentage <= targetPercentage){
663
- this.currentKeyframeIndex += 1;
664
- }
665
- }
666
- this.applyAnimationValue(value);
667
- if(this.localTime >= this._duration + this.dragTime + this.delayTime){
668
- // console.log("animation should end");
669
- this.playedTime += 1;
670
- this.endCallbacks.forEach((callback) => {
671
- queueMicrotask(()=>{callback()});
672
- });
673
- if(!this.loops || (this._maxLoopCount != undefined && this.playedTime >= this.maxLoopCount)){
674
- // this.onGoing = false;
675
- // console.log("animation should stop after ", this.playedTime, " loops");
676
- this.stop();
677
- } else {
678
- // console.log("animation should restart");
679
- this.onGoing = true;
680
- this.localTime = 0;
681
- this.currentKeyframeIndex = 0;
682
- this.start();
683
- }
684
- }
685
- // if((this.localTime >= this._duration + this.delayTime + this.dragTime) && this.loop){
686
- // // this.startAnimation();
687
- // this.localTime = 0;
688
- // this.onGoing = true;
689
- // this.currentKeyframeIndex = 0;
690
- // }
691
- }
692
- }
693
-
694
- findValue(valuePercentage: number, keyframes: Keyframe<T>[], animatableAttributeHelper: AnimatableAttributeHelper<T>): T{
695
- if(valuePercentage > 1){
696
- if(this.reverse){
697
- return animatableAttributeHelper.lerp(valuePercentage, keyframes[1], keyframes[0]);
698
- }
699
- return animatableAttributeHelper.lerp(valuePercentage, keyframes[keyframes.length - 2], keyframes[keyframes.length - 1]);
700
- }
701
- if(valuePercentage < 0){
702
- if(this.reverse){
703
- return animatableAttributeHelper.lerp(valuePercentage, keyframes[keyframes.length - 2], keyframes[keyframes.length - 1]);
704
- }
705
- return animatableAttributeHelper.lerp(valuePercentage, keyframes[1], keyframes[0]);
706
- }
707
- let left = 0;
708
- let right = keyframes.length - 1;
709
- while (left <= right) {
710
- let mid = left + Math.floor((right - left) / 2);
711
- const midPercentage = this.reverse ? 1 - keyframes[mid].percentage : keyframes[mid].percentage;
712
- if(midPercentage == valuePercentage) {
713
- return keyframes[mid].value;
714
- } else if(midPercentage < valuePercentage){
715
- if(this.reverse){
716
- right = mid - 1;
717
- } else {
718
- left = mid + 1;
719
- }
720
- } else {
721
- if(this.reverse){
722
- left = mid + 1;
723
- } else {
724
- right = mid - 1;
725
- }
726
- }
727
- }
728
- if(left > keyframes.length - 1){
729
- // excceding the keyframes
730
- left = keyframes.length - 1;
731
- }
732
- const interpolateStartFrame = this.reverse ? {percentage: 1 - keyframes[left].percentage, value: keyframes[left].value} : keyframes[left - 1];
733
- const interplateEndFrame = this.reverse ? {percentage: 1 - keyframes[left - 1].percentage, value: keyframes[left - 1].value} : keyframes[left];
734
- // return animatableAttributeHelper.lerp(valuePercentage, keyframes[left - 1], keyframes[left]);
735
- return animatableAttributeHelper.lerp(valuePercentage, interpolateStartFrame, interplateEndFrame);
736
- }
737
-
738
- setUp(): void {
739
- // this.applyAnimationValue(this.keyframes[0].value);
740
- this.setUpFn();
741
- }
742
-
743
- tearDown(): void {
744
- this.tearDownFn();
745
- }
746
-
747
- get loops(): boolean {
748
- return this.loop;
749
- }
750
-
751
- set loops(loop: boolean) {
752
- this.loop = loop;
753
- }
754
-
755
- get duration(): number {
756
- return this._duration + this.delayTime + this.dragTime;
757
- }
758
-
759
- set duration(duration: number) {
760
- if(duration < 0){
761
- return;
762
- }
763
- const originalDuration = this._duration + this.delayTime + this.dragTime;
764
- const scale = duration / originalDuration;
765
- const newDelayTime = this.delayTime * scale;
766
- const newDragTime = this.dragTime * scale;
767
- this.delayTime = newDelayTime;
768
- this.dragTime = newDragTime;
769
- this._duration = this._duration * scale;
770
- if(this.parent != undefined){
771
- this.parent.updateDuration();
772
- }
773
- }
774
-
775
- nonCascadingDuration(newDuration: number): void {
776
- if(newDuration < 0){
777
- return;
778
- }
779
- const originalDuration = this._duration + this.delayTime + this.dragTime;
780
- const scale = newDuration / originalDuration;
781
- const newDelayTime = this.delayTime * scale;
782
- const newDragTime = this.dragTime * scale;
783
- this.delayTime = newDelayTime;
784
- this.dragTime = newDragTime;
785
- this._duration = newDuration;
786
- }
787
-
788
- resetAnimationState(): void {
789
- this.onGoing = false;
790
- this.applyAnimationValue(this.keyframes[0].value);
791
- this.currentKeyframeIndex = 0;
792
- this.setUp();
793
- }
794
-
795
- wrapUp(): void {
796
- this.onGoing = false;
797
- this.localTime = this._duration + this.dragTime + this.delayTime + 0.1;
798
- this.currentKeyframeIndex = 0;
799
- }
800
-
801
- get delay(): number {
802
- return this.delayTime;
803
- }
804
-
805
- set delay(delayTime: number){
806
- this.delayTime = delayTime;
807
- if(this.parent != undefined){
808
- this.parent.updateDuration();
809
- }
810
- }
811
-
812
- get drag(): number {
813
- return this.dragTime;
814
- }
815
-
816
- set drag(dragTime: number){
817
- this.dragTime = dragTime;
818
- if(this.parent != undefined){
819
- this.parent.updateDuration();
820
- }
821
- }
822
-
823
- get trueDuration(): number {
824
- return this._duration;
825
- }
826
-
827
- set trueDuration(duration: number){
828
- this._duration = duration;
829
- if(this.parent != undefined){
830
- this.parent.updateDuration();
831
- }
832
- }
833
-
834
- setParent(parent: AnimatorContainer){
835
- this.parent = parent;
836
- }
837
-
838
- detachParent(): void {
839
- this.parent = undefined;
840
- }
841
-
842
- set keyFrames(keyFrames: Keyframe<T>[]){
843
- this.keyframes = keyFrames;
844
- this.zeroPercentageValue = this.findValue(0, keyFrames, this.animatableAttributeHelper);
845
- }
846
-
847
- get keyFrames(): Keyframe<T>[]{
848
- return this.keyframes;
849
- }
850
-
851
- get easeFunction(): (percentage: number) => number {
852
- return this.easeFn;
853
- }
854
-
855
- set easeFunction(easeFn: (percentage: number) => number){
856
- this.easeFn = easeFn;
857
- }
858
-
859
- onEnd(callback: Function): UnSubscribe{
860
- this.endCallbacks.push(callback);
861
- return ()=>{
862
- this.endCallbacks = this.endCallbacks.filter((cb) => cb != callback);
863
- }
864
- }
865
-
866
- onStart(callback: Function): UnSubscribe{
867
- this.startCallbacks.push(callback);
868
- return ()=>{
869
- this.startCallbacks = this.startCallbacks.filter((cb) => cb != callback);
870
- }
871
- }
872
-
873
- onStartAfterDelay(callback: Function): UnSubscribe{
874
- this.startAfterDelayCallbacks.push(callback);
875
- return ()=>{
876
- this.startAfterDelayCallbacks = this.startAfterDelayCallbacks.filter((cb) => cb != callback);
877
- }
878
- }
879
-
880
- clearOnEnd(): void {
881
- this.endCallbacks = [];
882
- }
883
-
884
- clearOnStart(): void {
885
- this.startCallbacks = [];
886
- }
887
-
888
- get maxLoopCount(): number | undefined {
889
- return this._maxLoopCount;
890
- }
891
-
892
- set maxLoopCount(maxLoopCount: number | undefined) {
893
- this._maxLoopCount = maxLoopCount;
894
- }
895
- }
896
-
897
- export interface Keyframes<T> {
898
- keyframes: Keyframe<T>[];
899
- from(value: T): Keyframes<T>;
900
- to(value: T): Keyframes<T>;
901
- insertAt(percentage: number, value: T): void;
902
- clearFrames(): void;
903
- }
904
-
905
- export class KeyFramesContiner<T> {
906
-
907
- private _keyframes: Keyframe<T>[];
908
-
909
- constructor(){
910
- this._keyframes = [];
911
- }
912
-
913
- get keyframes(): Keyframe<T>[] {
914
- return this._keyframes;
915
- }
916
-
917
- from(value: T): Keyframes<T>{
918
- if(this._keyframes.length == 0){
919
- this._keyframes.push({percentage: 0, value: value});
920
- } else {
921
- if(this._keyframes[0].percentage == 0){
922
- this._keyframes[0].value = value;
923
- } else {
924
- this._keyframes.unshift({percentage: 0, value: value});
925
- }
926
- }
927
- return this;
928
- }
929
-
930
- to(value: T): Keyframes<T>{
931
- if(this._keyframes.length == 0){
932
- this._keyframes.push({percentage: 1, value: value});
933
- } else {
934
- if(this._keyframes[this._keyframes.length - 1].percentage == 1){
935
- this._keyframes[this._keyframes.length - 1].value = value;
936
- } else {
937
- this._keyframes.push({percentage: 1, value: value});
938
- }
939
- }
940
- return this;
941
- }
942
-
943
- insertAt(percentage: number, value: T): void{
944
- this._keyframes.push({percentage: percentage, value: value});
945
- }
946
-
947
- clearFrames(): void{
948
- this._keyframes = [];
949
- }
950
- }