()
| 62 | Number.isFinite(timestamp) ? timestamp : null; |
| 63 | |
| 64 | export function createAnimationController(): AnimationController { |
| 65 | const animations = new Map<AnimationId, AnimationInternal>(); |
| 66 | |
| 67 | function animate( |
| 68 | from: number | ReadonlyArray<number>, |
| 69 | to: number | ReadonlyArray<number>, |
| 70 | duration: number, |
| 71 | easing: EasingFunction, |
| 72 | onUpdate: ((value: number) => void) | ((value: ReadonlyArray<number>) => void), |
| 73 | onComplete?: () => void, |
| 74 | ): AnimationId { |
| 75 | const id: AnimationId = Symbol('Animation'); |
| 76 | |
| 77 | if (Array.isArray(from) || Array.isArray(to)) { |
| 78 | if (!Array.isArray(from) || !Array.isArray(to)) { |
| 79 | throw new Error('Array animation requires both "from" and "to" to be arrays'); |
| 80 | } |
| 81 | if (from.length !== to.length) { |
| 82 | throw new Error( |
| 83 | `Array animation length mismatch: from.length=${from.length}, to.length=${to.length}`, |
| 84 | ); |
| 85 | } |
| 86 | |
| 87 | const out = new Array<number>(from.length); |
| 88 | animations.set(id, { |
| 89 | kind: 'array', |
| 90 | from, |
| 91 | to, |
| 92 | duration, |
| 93 | easing, |
| 94 | onUpdate: onUpdate as (value: ReadonlyArray<number>) => void, |
| 95 | onComplete, |
| 96 | startTime: null, |
| 97 | out, |
| 98 | }); |
| 99 | return id; |
| 100 | } |
| 101 | |
| 102 | animations.set(id, { |
| 103 | kind: 'scalar', |
| 104 | from: from as number, |
| 105 | to: to as number, |
| 106 | duration, |
| 107 | easing, |
| 108 | onUpdate: onUpdate as (value: number) => void, |
| 109 | onComplete, |
| 110 | startTime: null, |
| 111 | }); |
| 112 | return id; |
| 113 | } |
| 114 | |
| 115 | function cancel(animationId: AnimationId): void { |
| 116 | animations.delete(animationId); |
| 117 | } |
| 118 | |
| 119 | function cancelAll(): void { |
| 120 | animations.clear(); |
| 121 | } |
no outgoing calls
no test coverage detected