@omnimedia/omnitool 1.1.0-81 → 1.1.0-83

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 (48) hide show
  1. package/README.md +59 -6
  2. package/package.json +1 -1
  3. package/s/timeline/index.ts +3 -2
  4. package/s/timeline/parts/animations/make.ts +34 -0
  5. package/s/timeline/parts/animations/presets.ts +138 -0
  6. package/s/timeline/parts/{animations.ts → animations/properties.ts} +2 -24
  7. package/s/timeline/parts/animations/registry.ts +13 -0
  8. package/s/timeline/parts/animations/types.ts +68 -0
  9. package/s/timeline/renderers/parts/handy.ts +2 -2
  10. package/s/timeline/sugar/helpers.ts +32 -18
  11. package/s/timeline/sugar/o.ts +25 -17
  12. package/s/timeline/utils/anim.ts +3 -3
  13. package/x/demo/demo.bundle.min.js +100 -100
  14. package/x/demo/demo.bundle.min.js.map +4 -4
  15. package/x/index.html +2 -2
  16. package/x/tests.bundle.min.js +102 -102
  17. package/x/tests.bundle.min.js.map +4 -4
  18. package/x/tests.html +1 -1
  19. package/x/timeline/index.d.ts +3 -2
  20. package/x/timeline/index.js +3 -2
  21. package/x/timeline/index.js.map +1 -1
  22. package/x/timeline/parts/animations/make.d.ts +3 -0
  23. package/x/timeline/parts/animations/make.js +23 -0
  24. package/x/timeline/parts/animations/make.js.map +1 -0
  25. package/x/timeline/parts/animations/presets.d.ts +170 -0
  26. package/x/timeline/parts/animations/presets.js +135 -0
  27. package/x/timeline/parts/animations/presets.js.map +1 -0
  28. package/x/timeline/parts/{animations.d.ts → animations/properties.d.ts} +1 -20
  29. package/x/timeline/parts/{animations.js → animations/properties.js} +2 -2
  30. package/x/timeline/parts/animations/properties.js.map +1 -0
  31. package/x/timeline/parts/animations/registry.d.ts +218 -0
  32. package/x/timeline/parts/animations/registry.js +11 -0
  33. package/x/timeline/parts/animations/registry.js.map +1 -0
  34. package/x/timeline/parts/animations/types.d.ts +50 -0
  35. package/x/timeline/parts/animations/types.js +2 -0
  36. package/x/timeline/parts/animations/types.js.map +1 -0
  37. package/x/timeline/renderers/parts/handy.js +2 -2
  38. package/x/timeline/renderers/parts/handy.js.map +1 -1
  39. package/x/timeline/sugar/helpers.d.ts +2 -1
  40. package/x/timeline/sugar/helpers.js +23 -16
  41. package/x/timeline/sugar/helpers.js.map +1 -1
  42. package/x/timeline/sugar/o.d.ts +1 -0
  43. package/x/timeline/sugar/o.js +18 -16
  44. package/x/timeline/sugar/o.js.map +1 -1
  45. package/x/timeline/utils/anim.d.ts +3 -3
  46. package/x/timeline/utils/anim.js +1 -1
  47. package/x/timeline/utils/anim.js.map +1 -1
  48. package/x/timeline/parts/animations.js.map +0 -1
package/README.md CHANGED
@@ -150,7 +150,7 @@ Animated spatial transforms:
150
150
 
151
151
  ```ts
152
152
  const timeline = omni.timeline(o => {
153
- const slideIn = o.animatedSpatial(
153
+ const customMotion = o.animatedSpatial(
154
154
  o.anim.transform("linear", [
155
155
  [0, o.transform({position: [-400, 0]})],
156
156
  [1000, o.transform({position: [0, 0]})],
@@ -161,7 +161,7 @@ const timeline = omni.timeline(o => {
161
161
  duration: 2000,
162
162
  styles: {fill: "white", fontSize: 36}
163
163
  })
164
- o.set(title.id, {spatialId: slideIn.id})
164
+ o.set(title.id, {spatialId: customMotion.id})
165
165
 
166
166
  return o.stack(
167
167
  o.video(clip, {duration: 4000}),
@@ -170,6 +170,29 @@ const timeline = omni.timeline(o => {
170
170
  })
171
171
  ```
172
172
 
173
+ Built-in spatial animations:
174
+
175
+ ```ts
176
+ const slideIn = o.animatedSpatial(o.anim.presets.slideIn())
177
+ const slideOut = o.animatedSpatial(o.anim.presets.slideOut({duration: 500}))
178
+ const spinIn = o.animatedSpatial(o.anim.presets.spinIn())
179
+ const spinOut = o.animatedSpatial(o.anim.presets.spinOut())
180
+ const zoomIn = o.animatedSpatial(o.anim.presets.zoomIn())
181
+ const zoomOut = o.animatedSpatial(o.anim.presets.zoomOut())
182
+ const bounceIn = o.animatedSpatial(o.anim.presets.bounceIn())
183
+ const bounceOut = o.animatedSpatial(o.anim.presets.bounceOut())
184
+ ```
185
+
186
+ Built-in scalar animations:
187
+
188
+ ```ts
189
+ const fadeInPreset = o.anim.presets.fadeIn()
190
+ const fadeIn = o.animate.opacity.make(fadeInPreset.terp, fadeInPreset.track)
191
+
192
+ const fadeOutPreset = o.anim.presets.fadeOut({duration: 500})
193
+ const fadeOut = o.animate.opacity.make(fadeOutPreset.terp, fadeOutPreset.track)
194
+ ```
195
+
173
196
  Animation application:
174
197
 
175
198
  ```ts
@@ -215,19 +238,49 @@ const timeline = omni.timeline(o => {
215
238
  })
216
239
  ```
217
240
 
218
- Animation registry:
241
+ Animation metadata:
219
242
 
220
243
  ```ts
221
- import {animations} from "@omnimedia/omnitool"
244
+ import {animatableProperties, animationPresets} from "@omnimedia/omnitool"
222
245
 
223
- Object.entries(animations).forEach(([property, meta]) => {
246
+ Object.entries(animatableProperties).forEach(([property, meta]) => {
224
247
  console.log(property, meta.type, meta.defaultTerp, meta.channels)
225
248
  // transform transform linear [...]
226
249
  // opacity scalar linear [...]
227
250
  })
251
+
252
+ Object.entries(animationPresets).forEach(([preset, meta]) => {
253
+ console.log(preset, meta.type, meta.label, meta.defaults)
254
+ // slideIn motion Slide in {...}
255
+ // slideOut motion Slide out {...}
256
+ // spinIn motion Spin in {...}
257
+ // spinOut motion Spin out {...}
258
+ // zoomIn motion Zoom in {...}
259
+ // zoomOut motion Zoom out {...}
260
+ // bounceIn motion Bounce in {...}
261
+ // bounceOut motion Bounce out {...}
262
+ // fadeIn scalar Fade in {...}
263
+ // fadeOut scalar Fade out {...}
264
+ })
265
+ ```
266
+
267
+ Animatable properties describe what can be keyframed, such as `transform` and `opacity`.
268
+ Animation presets describe built-in recipes, such as `slideIn` and `fadeIn`, that create animation data.
269
+ Use `animationPresets` to list available recipes, and `o.anim.presets` to create animation data from them.
270
+
271
+ Utils:
272
+
273
+ ```ts
274
+ import {resolveScalarAnimation, resolveTransformAnimation} from "@omnimedia/omnitool"
275
+
276
+ const transform = resolveTransformAnimation(localTime, spatial.anim)
277
+ const opacity = resolveScalarAnimation(localTime, opacityAnimation)
228
278
  ```
229
279
 
230
- Each animation definition describes the semantic shape of the animation: its value kind, default interpolation, and numeric channels with defaults and units. This is useful for tools that need to create valid keyframes without hardcoding Omnitool's track layout.
280
+ `resolveTransformAnimation` resolves an animated transform at the given local time.
281
+ `resolveScalarAnimation` resolves an animated scalar value at the given local time.
282
+ `localTime` is time relative to the item being resolved.
283
+ `clamp` is the default and currently only extrapolation mode, holding the first or last keyframe value outside the authored range.
231
284
 
232
285
  Worker URL notes:
233
286
  - `Driver.setup()` defaults to `/node_modules/@omnimedia/omnitool/x/driver/driver.worker.bundle.min.js`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnimedia/omnitool",
3
- "version": "1.1.0-81",
3
+ "version": "1.1.0-83",
4
4
  "description": "open source video processing tools",
5
5
  "license": "MIT",
6
6
  "author": "Przemysław Gałęzki",
@@ -1,13 +1,13 @@
1
1
 
2
+ export * from "./types.js"
2
3
  export * from "./parts/basics.js"
3
- export * from "./parts/animations.js"
4
4
  export * from "./parts/filters.js"
5
5
  export * from "./parts/item.js"
6
6
  export * from "./parts/media.js"
7
7
  export * from "./parts/resource-pool.js"
8
8
  export * from "./parts/resource.js"
9
9
  export * from "./parts/filmstrip.js"
10
- export * from "./types.js"
10
+ export * from "./parts/animations/registry.js"
11
11
 
12
12
  export * from "./parts/waveform/waveform.js"
13
13
  export * from "./parts/waveform/parts/types.js"
@@ -19,3 +19,4 @@ export * from "./sugar/omni.js"
19
19
 
20
20
  export * from "./utils/checksum.js"
21
21
  export * from "./utils/datafile.js"
22
+ export * from "./utils/anim.js"
@@ -0,0 +1,34 @@
1
+
2
+ import {animationPresets} from "./presets.js"
3
+ import {AnimationPresetActions, MotionAnimationOptions, ScalarAnimationOptions} from "./types.js"
4
+ import {Anim, Interpolation, Keyframes, TrackTransform, Transform, TransformOptions} from "../../types.js"
5
+
6
+ export function makeAnimationPresets(
7
+ scalar: (terp: Interpolation, track: Keyframes) => Anim<Keyframes>,
8
+ transform: (terp: Interpolation, source: Keyframes<Transform>) => Anim<TrackTransform>,
9
+ transformFrom: (options: TransformOptions) => Transform,
10
+ ): AnimationPresetActions {
11
+ const entries = Object.entries(animationPresets).map(([name, preset]) => {
12
+ const action = preset.type === "motion"
13
+ ? (options?: MotionAnimationOptions): Anim<TrackTransform> =>
14
+ transform(options?.terp ?? preset.defaults.terp, [
15
+ [0, transformFrom({
16
+ ...preset.transform.from,
17
+ ...(options?.from === undefined ? {} : {position: options.from}),
18
+ })],
19
+ [options?.duration ?? preset.defaults.duration, transformFrom({
20
+ ...preset.transform.to,
21
+ ...(options?.to === undefined ? {} : {position: options.to}),
22
+ })],
23
+ ])
24
+ : (options?: ScalarAnimationOptions): Anim<Keyframes> =>
25
+ scalar(options?.terp ?? preset.defaults.terp, [
26
+ [0, options?.from ?? preset.defaults.from],
27
+ [options?.duration ?? preset.defaults.duration, options?.to ?? preset.defaults.to],
28
+ ])
29
+
30
+ return [name, action]
31
+ })
32
+
33
+ return Object.fromEntries(entries) as AnimationPresetActions
34
+ }
@@ -0,0 +1,138 @@
1
+
2
+ import type {AnimationPresetDefinition} from "./types.js"
3
+
4
+ export const animationPresets = {
5
+ slideIn: {
6
+ type: "motion",
7
+ label: "Slide in",
8
+ defaults: {
9
+ duration: 700,
10
+ from: [-400, 0],
11
+ to: [0, 0],
12
+ terp: "easeOut",
13
+ },
14
+ transform: {
15
+ from: {position: [-400, 0]},
16
+ to: {position: [0, 0]},
17
+ },
18
+ },
19
+ slideOut: {
20
+ type: "motion",
21
+ label: "Slide out",
22
+ defaults: {
23
+ duration: 700,
24
+ from: [0, 0],
25
+ to: [400, 0],
26
+ terp: "easeIn",
27
+ },
28
+ transform: {
29
+ from: {position: [0, 0]},
30
+ to: {position: [400, 0]},
31
+ },
32
+ },
33
+ spinIn: {
34
+ type: "motion",
35
+ label: "Spin in",
36
+ defaults: {
37
+ duration: 700,
38
+ from: [0, 0],
39
+ to: [0, 0],
40
+ terp: "easeOut",
41
+ },
42
+ transform: {
43
+ from: {scale: [0, 0], rotation: -Math.PI},
44
+ to: {scale: [1, 1], rotation: 0},
45
+ },
46
+ },
47
+ spinOut: {
48
+ type: "motion",
49
+ label: "Spin out",
50
+ defaults: {
51
+ duration: 700,
52
+ from: [0, 0],
53
+ to: [0, 0],
54
+ terp: "easeIn",
55
+ },
56
+ transform: {
57
+ from: {scale: [1, 1], rotation: 0},
58
+ to: {scale: [0, 0], rotation: Math.PI},
59
+ },
60
+ },
61
+ zoomIn: {
62
+ type: "motion",
63
+ label: "Zoom in",
64
+ defaults: {
65
+ duration: 700,
66
+ from: [0, 0],
67
+ to: [0, 0],
68
+ terp: "easeOut",
69
+ },
70
+ transform: {
71
+ from: {scale: [0, 0]},
72
+ to: {scale: [1, 1]},
73
+ },
74
+ },
75
+ zoomOut: {
76
+ type: "motion",
77
+ label: "Zoom out",
78
+ defaults: {
79
+ duration: 700,
80
+ from: [0, 0],
81
+ to: [0, 0],
82
+ terp: "easeIn",
83
+ },
84
+ transform: {
85
+ from: {scale: [1, 1]},
86
+ to: {scale: [0, 0]},
87
+ },
88
+ },
89
+ bounceIn: {
90
+ type: "motion",
91
+ label: "Bounce in",
92
+ defaults: {
93
+ duration: 700,
94
+ from: [0, 0],
95
+ to: [0, 0],
96
+ terp: "bounce",
97
+ },
98
+ transform: {
99
+ from: {scale: [0, 0]},
100
+ to: {scale: [1, 1]},
101
+ },
102
+ },
103
+ bounceOut: {
104
+ type: "motion",
105
+ label: "Bounce out",
106
+ defaults: {
107
+ duration: 700,
108
+ from: [0, 0],
109
+ to: [0, 0],
110
+ terp: "bounce",
111
+ },
112
+ transform: {
113
+ from: {scale: [1, 1]},
114
+ to: {scale: [0, 0]},
115
+ },
116
+ },
117
+ fadeIn: {
118
+ type: "scalar",
119
+ label: "Fade in",
120
+ defaults: {
121
+ duration: 700,
122
+ from: 0,
123
+ to: 1,
124
+ terp: "easeIn",
125
+ },
126
+ },
127
+ fadeOut: {
128
+ type: "scalar",
129
+ label: "Fade out",
130
+ defaults: {
131
+ duration: 700,
132
+ from: 1,
133
+ to: 0,
134
+ terp: "easeOut",
135
+ },
136
+ },
137
+ } as const satisfies Record<string, AnimationPresetDefinition>
138
+
@@ -1,23 +1,5 @@
1
1
 
2
- import type {Interpolation} from "../types.js"
3
-
4
- export type AnimationType = "scalar" | "transform"
5
- export type AnimationChannelType = "number"
6
- export type AnimationUnit = "pixel" | "scale" | "radian" | "ratio"
7
-
8
- export type AnimationChannel = {
9
- path?: string
10
- type: AnimationChannelType
11
- default: number
12
- unit?: AnimationUnit
13
- range?: readonly [min: number, max: number]
14
- }
15
-
16
- export type AnimationDefinition = {
17
- type: AnimationType
18
- defaultTerp: Interpolation
19
- channels: readonly AnimationChannel[]
20
- }
2
+ import type {AnimationDefinition} from "./types.js"
21
3
 
22
4
  export const spatialAnimations = {
23
5
  transform: {
@@ -45,12 +27,8 @@ export const visualAnimations = {
45
27
 
46
28
  // const audioAnimations = {}
47
29
 
48
- export const animations = {
30
+ export const animatableProperties = {
49
31
  ...spatialAnimations,
50
32
  ...visualAnimations,
51
33
  } as const
52
34
 
53
- export type SpatialAnimationProperty = keyof typeof spatialAnimations
54
- export type VisualAnimationProperty = keyof typeof visualAnimations
55
- export type AnimationProperty = keyof typeof animations
56
-
@@ -0,0 +1,13 @@
1
+
2
+ import {animationPresets} from "./presets.js"
3
+ import {animatableProperties} from "./properties.js"
4
+
5
+ export const animationRegistry = {
6
+ presets: animationPresets,
7
+ properties: animatableProperties,
8
+ } as const
9
+
10
+ export * from "./make.js"
11
+ export * from "./presets.js"
12
+ export * from "./properties.js"
13
+ export * from "./types.js"
@@ -0,0 +1,68 @@
1
+
2
+ import {animationPresets} from "./presets.js"
3
+ import {spatialAnimations, visualAnimations, animatableProperties} from "./properties.js"
4
+ import {Anim, Interpolation, Keyframes, TrackTransform, TransformOptions, Vec2} from "../../types.js"
5
+
6
+ export type AnimationType = "scalar" | "transform"
7
+ export type AnimationChannelType = "number"
8
+ export type AnimationUnit = "pixel" | "scale" | "radian" | "ratio"
9
+
10
+ export type AnimationChannel = {
11
+ path?: string
12
+ type: AnimationChannelType
13
+ default: number
14
+ unit?: AnimationUnit
15
+ range?: readonly [min: number, max: number]
16
+ }
17
+
18
+ export type AnimationDefinition = {
19
+ type: AnimationType
20
+ defaultTerp: Interpolation
21
+ channels: readonly AnimationChannel[]
22
+ }
23
+
24
+ export type MotionAnimationPresetDefinition = {
25
+ type: "motion"
26
+ label: string
27
+ defaults: Required<MotionAnimationOptions>
28
+ transform: {
29
+ from: TransformOptions
30
+ to: TransformOptions
31
+ }
32
+ }
33
+
34
+ export type ScalarAnimationPresetDefinition = {
35
+ type: "scalar"
36
+ label: string
37
+ defaults: Required<ScalarAnimationOptions>
38
+ }
39
+
40
+ export type AnimationPresetDefinition =
41
+ | MotionAnimationPresetDefinition
42
+ | ScalarAnimationPresetDefinition
43
+
44
+ export type SpatialAnimationProperty = keyof typeof spatialAnimations
45
+ export type VisualAnimationProperty = keyof typeof visualAnimations
46
+ export type AnimationProperty = keyof typeof animatableProperties
47
+ export type AnimationPreset = keyof typeof animationPresets
48
+
49
+ type AnimationPresetAction<TPreset extends AnimationPresetDefinition> =
50
+ TPreset extends MotionAnimationPresetDefinition
51
+ ? (options?: MotionAnimationOptions) => Anim<TrackTransform>
52
+ : TPreset extends ScalarAnimationPresetDefinition
53
+ ? (options?: ScalarAnimationOptions) => Anim<Keyframes>
54
+ : never
55
+
56
+ export type AnimationPresetActions = {
57
+ [TName in AnimationPreset]: AnimationPresetAction<(typeof animationPresets)[TName]>
58
+ }
59
+
60
+ export type AnimationPresetOptions<Value> = {
61
+ duration?: number
62
+ from?: Value
63
+ to?: Value
64
+ terp?: Interpolation
65
+ }
66
+
67
+ export type MotionAnimationOptions = AnimationPresetOptions<Vec2>
68
+ export type ScalarAnimationOptions = AnimationPresetOptions<number>
@@ -3,7 +3,7 @@ import {ms, Ms} from '../../../units/ms.js'
3
3
  import {Id, TimelineFile} from '../../parts/basics.js'
4
4
  import { SampleContext } from './samplers/visual/parts/types.js'
5
5
  import {I6, Mat6, mul6, transformToMat6} from '../../utils/matrix.js'
6
- import {resolveScalarAnimation, resolveSpatialTransform} from '../../utils/anim.js'
6
+ import {resolveScalarAnimation, resolveTransform} from '../../utils/anim.js'
7
7
  import {ContainerItem, Item, Kind, PlayableItem, SpatialItem} from '../../parts/item.js'
8
8
 
9
9
  function isPlayableItem(item: Item.Any): item is PlayableItem {
@@ -101,7 +101,7 @@ function applySpatialIfAny(
101
101
  if ("spatialId" in item && item.spatialId) {
102
102
  const spatial = items.get(item.spatialId) as SpatialItem | undefined
103
103
  if (spatial?.enabled) {
104
- const local = transformToMat6(resolveSpatialTransform(spatial, time))
104
+ const local = transformToMat6(resolveTransform(spatial, time))
105
105
  return mul6(local, parentMatrix)
106
106
  }
107
107
  }
@@ -5,10 +5,34 @@ import {O} from "./o.js"
5
5
  import {Media} from "../parts/media.js"
6
6
  import {TimelineFile} from "../parts/basics.js"
7
7
  import {FilterAction} from "../parts/filters.js"
8
- import {visualAnimations} from "../parts/animations.js"
9
8
  import {filters, FilterParams, FilterType} from "../parts/filters.js"
10
9
  import {Crop, FilterableItem, Item, VisualAnimatableItem} from "../parts/item.js"
11
- import {Anim, AnimateAction, Interpolation, Keyframes, TrackTransform, Transform, Vec2, VisualAnimations} from "../types.js"
10
+ import {makeAnimationPresets, visualAnimations} from "../parts/animations/registry.js"
11
+ import {Anim, AnimateAction, Interpolation, Keyframes, TrackTransform, Transform, TransformOptions, Vec2, VisualAnimations} from "../types.js"
12
+
13
+ const transformFrom = (options: TransformOptions): Transform => [
14
+ options.position ?? [0, 0],
15
+ options.scale ?? [1, 1],
16
+ options.rotation ?? 0,
17
+ ]
18
+
19
+ const transformAnimation = (terp: Interpolation, source: Keyframes<Transform>): Anim<TrackTransform> => {
20
+ const track: TrackTransform = {
21
+ position: {x: [], y: []},
22
+ scale: {x: [], y: []},
23
+ rotation: [],
24
+ }
25
+
26
+ for (const [time, [position, scale, rotation]] of source) {
27
+ track.position.x.push([time, position[0]])
28
+ track.position.y.push([time, position[1]])
29
+ track.scale.x.push([time, scale[0]])
30
+ track.scale.y.push([time, scale[1]])
31
+ track.rotation.push([time, rotation])
32
+ }
33
+
34
+ return {terp, track}
35
+ }
12
36
 
13
37
  export type Build<T extends Item.Any = Item.Any> = (o: O) => T
14
38
  type BuildVisualAnimateActions = {
@@ -99,23 +123,13 @@ export const anim = {
99
123
  return {terp, track}
100
124
  },
101
125
 
102
- transform(terp: Interpolation, source: Keyframes<Transform>): Anim<TrackTransform> {
103
- const track: TrackTransform = {
104
- position: {x: [], y: []},
105
- scale: {x: [], y: []},
106
- rotation: [],
107
- }
108
-
109
- for (const [time, [position, scale, rotation]] of source) {
110
- track.position.x.push([time, position[0]])
111
- track.position.y.push([time, position[1]])
112
- track.scale.x.push([time, scale[0]])
113
- track.scale.y.push([time, scale[1]])
114
- track.rotation.push([time, rotation])
115
- }
126
+ transform: transformAnimation,
116
127
 
117
- return {terp, track}
118
- },
128
+ presets: makeAnimationPresets(
129
+ (terp, track) => ({terp, track}),
130
+ transformAnimation,
131
+ transformFrom,
132
+ ),
119
133
  }
120
134
 
121
135
  interface BuildFilterAction<TFilter extends FilterType> {
@@ -5,7 +5,7 @@ import {Media} from "../parts/media.js"
5
5
  import {Id, TimelineFile} from "../parts/basics.js"
6
6
  import {FilterAction, FilterActions} from "../parts/filters.js"
7
7
  import {filters, FilterParams, FilterType} from "../parts/filters.js"
8
- import {visualAnimations} from "../parts/animations.js"
8
+ import {makeAnimationPresets, visualAnimations} from "../parts/animations/registry.js"
9
9
  import {Crop, Effect, FilterableItem, Item, Kind, VisualAnimatableItem} from "../parts/item.js"
10
10
  import {Anim, AnimateAction, Interpolation, Keyframes, TrackTransform, Transform, TransformOptions, Vec2, VisualAnimations} from "../types.js"
11
11
 
@@ -87,6 +87,24 @@ export class O {
87
87
  return item
88
88
  }
89
89
 
90
+ #transformAnimation = (terp: Interpolation, source: Keyframes<Transform>): Anim<TrackTransform> => {
91
+ const track: TrackTransform = {
92
+ position: {x: [], y: []},
93
+ scale: {x: [], y: []},
94
+ rotation: [],
95
+ }
96
+
97
+ for (const [time, [position, scale, rotation]] of source) {
98
+ track.position.x.push([time, position[0]])
99
+ track.position.y.push([time, position[1]])
100
+ track.scale.x.push([time, scale[0]])
101
+ track.scale.y.push([time, scale[1]])
102
+ track.rotation.push([time, rotation])
103
+ }
104
+
105
+ return {terp, track}
106
+ }
107
+
90
108
  anim = {
91
109
  scalar: (terp: Interpolation, track: Keyframes): Anim<Keyframes> => ({terp, track}),
92
110
 
@@ -101,23 +119,13 @@ export class O {
101
119
  return {terp, track}
102
120
  },
103
121
 
104
- transform: (terp: Interpolation, source: Keyframes<Transform>): Anim<TrackTransform> => {
105
- const track: TrackTransform = {
106
- position: {x: [], y: []},
107
- scale: {x: [], y: []},
108
- rotation: [],
109
- }
110
-
111
- for (const [time, [position, scale, rotation]] of source) {
112
- track.position.x.push([time, position[0]])
113
- track.position.y.push([time, position[1]])
114
- track.scale.x.push([time, scale[0]])
115
- track.scale.y.push([time, scale[1]])
116
- track.rotation.push([time, rotation])
117
- }
122
+ transform: this.#transformAnimation,
118
123
 
119
- return {terp, track}
120
- },
124
+ presets: makeAnimationPresets(
125
+ (terp, track) => ({terp, track}),
126
+ this.#transformAnimation,
127
+ options => this.transform(options),
128
+ ),
121
129
  }
122
130
 
123
131
  #makeFilter = <TFilter extends FilterType>(type: TFilter) => {
@@ -1,7 +1,7 @@
1
1
 
2
2
  import {resolveTerp} from "./terps.js"
3
3
  import {Item, Kind} from "../parts/item.js"
4
- import {Anim, Keyframes, ScalarAnimation, TrackTransform, Transform} from "../types.js"
4
+ import {Anim, Keyframes, ScalarAnimation, Transform, TransformAnimation} from "../types.js"
5
5
 
6
6
  const resolveScalar =(
7
7
  time: number,
@@ -44,7 +44,7 @@ const resolveScalar =(
44
44
 
45
45
  export const resolveTransformAnimation =(
46
46
  time: number,
47
- anim: Anim<TrackTransform>,
47
+ anim: TransformAnimation,
48
48
  ): Transform => ([
49
49
  [
50
50
  resolveScalar(time, anim.track.position.x, anim.terp),
@@ -62,7 +62,7 @@ export const resolveScalarAnimation =(
62
62
  anim: ScalarAnimation,
63
63
  ): number => resolveScalar(time, anim.track, anim.terp)
64
64
 
65
- export const resolveSpatialTransform =(
65
+ export const resolveTransform =(
66
66
  spatial: Item.Spatial | Item.AnimatedSpatial,
67
67
  time: number,
68
68
  ): Transform =>