@omnimedia/omnitool 1.1.0-78 → 1.1.0-79
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 +107 -1
- package/package.json +1 -1
- package/s/demo/routines/timeline-setup.ts +37 -22
- package/s/driver/fns/schematic.ts +2 -0
- package/s/driver/parts/compositor.ts +6 -0
- package/s/timeline/index.ts +2 -0
- package/s/timeline/parts/animations.ts +24 -0
- package/s/timeline/parts/item.ts +24 -1
- package/s/timeline/renderers/parts/handy.ts +53 -36
- package/s/timeline/renderers/parts/samplers/visual/parts/sample.ts +9 -8
- package/s/timeline/renderers/parts/samplers/visual/parts/sequence.ts +4 -4
- package/s/timeline/sugar/helpers.ts +74 -2
- package/s/timeline/sugar/o.ts +95 -2
- package/s/timeline/types.ts +26 -3
- package/s/timeline/utils/anim.ts +71 -0
- package/s/timeline/utils/terps.ts +81 -0
- package/x/demo/demo.bundle.min.js +104 -104
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/routines/timeline-setup.js +16 -4
- package/x/demo/routines/timeline-setup.js.map +1 -1
- package/x/driver/fns/schematic.d.ts +2 -0
- package/x/driver/parts/compositor.js +5 -0
- package/x/driver/parts/compositor.js.map +1 -1
- package/x/index.html +2 -2
- package/x/tests.bundle.min.js +105 -105
- package/x/tests.bundle.min.js.map +4 -4
- package/x/tests.html +1 -1
- package/x/timeline/index.d.ts +2 -0
- package/x/timeline/index.js +2 -0
- package/x/timeline/index.js.map +1 -1
- package/x/timeline/parts/animations.d.ts +25 -0
- package/x/timeline/parts/animations.js +12 -0
- package/x/timeline/parts/animations.js.map +1 -0
- package/x/timeline/parts/item.d.ts +24 -5
- package/x/timeline/parts/item.js +5 -3
- package/x/timeline/parts/item.js.map +1 -1
- package/x/timeline/renderers/parts/handy.d.ts +13 -7
- package/x/timeline/renderers/parts/handy.js +22 -20
- package/x/timeline/renderers/parts/handy.js.map +1 -1
- package/x/timeline/renderers/parts/samplers/visual/parts/sample.d.ts +3 -2
- package/x/timeline/renderers/parts/samplers/visual/parts/sample.js +6 -5
- package/x/timeline/renderers/parts/samplers/visual/parts/sample.js.map +1 -1
- package/x/timeline/renderers/parts/samplers/visual/parts/sequence.d.ts +3 -2
- package/x/timeline/renderers/parts/samplers/visual/parts/sequence.js +2 -2
- package/x/timeline/renderers/parts/samplers/visual/parts/sequence.js.map +1 -1
- package/x/timeline/sugar/helpers.d.ts +19 -2
- package/x/timeline/sugar/helpers.js +43 -0
- package/x/timeline/sugar/helpers.js.map +1 -1
- package/x/timeline/sugar/o.d.ts +18 -1
- package/x/timeline/sugar/o.js +71 -1
- package/x/timeline/sugar/o.js.map +1 -1
- package/x/timeline/types.d.ts +12 -2
- package/x/timeline/utils/anim.d.ts +5 -0
- package/x/timeline/utils/anim.js +44 -0
- package/x/timeline/utils/anim.js.map +1 -0
- package/x/timeline/utils/terps.d.ts +11 -0
- package/x/timeline/utils/terps.js +57 -0
- package/x/timeline/utils/terps.js.map +1 -0
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
import {TextStyleOptions} from "pixi.js"
|
|
3
3
|
|
|
4
4
|
import {O} from "./o.js"
|
|
5
|
-
import {Transform} from "../types.js"
|
|
6
5
|
import {Media} from "../parts/media.js"
|
|
7
6
|
import {TimelineFile} from "../parts/basics.js"
|
|
8
7
|
import {FilterAction} from "../parts/filters.js"
|
|
9
|
-
import {
|
|
8
|
+
import {visualAnimations} from "../parts/animations.js"
|
|
10
9
|
import {filters, FilterParams, FilterType} from "../parts/filters.js"
|
|
10
|
+
import {Crop, FilterableItem, Item, VisualAnimatableItem} from "../parts/item.js"
|
|
11
|
+
import {Anim, AnimateAction, Interpolation, Keyframes, TrackTransform, Transform, Vec2, VisualAnimations} from "../types.js"
|
|
11
12
|
|
|
12
13
|
export type Build<T extends Item.Any = Item.Any> = (o: O) => T
|
|
14
|
+
type BuildVisualAnimateActions = {
|
|
15
|
+
[TKey in keyof VisualAnimations]-?: BuildAnimateAction
|
|
16
|
+
}
|
|
13
17
|
|
|
14
18
|
function createTimeline(): TimelineFile {
|
|
15
19
|
return {
|
|
@@ -75,11 +79,59 @@ export function spatial(transform?: Transform, crop?: Crop): Build<Item.Spatial>
|
|
|
75
79
|
return o => o.spatial(transform, crop)
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
export function animatedSpatial(anim: Anim<TrackTransform>, crop?: Crop): Build<Item.AnimatedSpatial> {
|
|
83
|
+
return o => o.animatedSpatial(anim, crop)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const anim = {
|
|
87
|
+
scalar(terp: Interpolation, track: Keyframes): Anim<Keyframes> {
|
|
88
|
+
return {terp, track}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
vec2(terp: Interpolation, source: Keyframes<Vec2>): Anim<{x: Keyframes, y: Keyframes}> {
|
|
92
|
+
const track = {x: [] as Keyframes, y: [] as Keyframes}
|
|
93
|
+
|
|
94
|
+
for (const [time, [x, y]] of source) {
|
|
95
|
+
track.x.push([time, x])
|
|
96
|
+
track.y.push([time, y])
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {terp, track}
|
|
100
|
+
},
|
|
101
|
+
|
|
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
|
+
}
|
|
116
|
+
|
|
117
|
+
return {terp, track}
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
78
121
|
interface BuildFilterAction<TFilter extends FilterType> {
|
|
79
122
|
<T extends FilterableItem>(item: Build<T>, params?: FilterParams<TFilter>): Build<T>
|
|
80
123
|
make(params?: FilterParams<TFilter>): Build<Item.Filter<TFilter>>
|
|
81
124
|
}
|
|
82
125
|
|
|
126
|
+
interface BuildAnimateAction {
|
|
127
|
+
<T extends VisualAnimatableItem>(
|
|
128
|
+
item: Build<T>,
|
|
129
|
+
terp: Interpolation,
|
|
130
|
+
track: Keyframes
|
|
131
|
+
): Build<T>
|
|
132
|
+
make(terp: Interpolation, track: Keyframes): Build<Item.Animation>
|
|
133
|
+
}
|
|
134
|
+
|
|
83
135
|
type BuildFilterActions = {
|
|
84
136
|
[TName in keyof typeof filters]: BuildFilterAction<(typeof filters)[TName]["type"]>
|
|
85
137
|
}
|
|
@@ -106,6 +158,26 @@ function makeFilters(): BuildFilterActions {
|
|
|
106
158
|
|
|
107
159
|
export const filter = makeFilters()
|
|
108
160
|
|
|
161
|
+
function makeAnimate(
|
|
162
|
+
get: (o: O) => AnimateAction
|
|
163
|
+
): BuildAnimateAction {
|
|
164
|
+
const action = (<T extends VisualAnimatableItem>(
|
|
165
|
+
item: Build<T>,
|
|
166
|
+
terp: Interpolation,
|
|
167
|
+
track: Keyframes
|
|
168
|
+
): Build<T> => o => get(o)(item(o), terp, track)) as BuildAnimateAction
|
|
169
|
+
action.make = (terp: Interpolation, track: Keyframes) => o => get(o).make(terp, track)
|
|
170
|
+
return action
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function makeAnimateActions(): BuildVisualAnimateActions {
|
|
174
|
+
const entries = Object.keys(visualAnimations)
|
|
175
|
+
.map(key => [key, makeAnimate(o => o.animate[key as keyof VisualAnimations] as AnimateAction)])
|
|
176
|
+
return Object.fromEntries(entries) as BuildVisualAnimateActions
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const animate = makeAnimateActions()
|
|
180
|
+
|
|
109
181
|
export function textStyle(style: TextStyleOptions): Build<Item.TextStyle> {
|
|
110
182
|
return o => o.textStyle(style)
|
|
111
183
|
}
|
package/s/timeline/sugar/o.ts
CHANGED
|
@@ -3,10 +3,15 @@ import {TextStyleOptions} from "pixi.js"
|
|
|
3
3
|
|
|
4
4
|
import {Media} from "../parts/media.js"
|
|
5
5
|
import {Id, TimelineFile} from "../parts/basics.js"
|
|
6
|
-
import {Transform, TransformOptions, Vec2} from "../types.js"
|
|
7
6
|
import {FilterAction, FilterActions} from "../parts/filters.js"
|
|
8
|
-
import {Crop, Effect, FilterableItem, Item, Kind} from "../parts/item.js"
|
|
9
7
|
import {filters, FilterParams, FilterType} from "../parts/filters.js"
|
|
8
|
+
import {visualAnimations} from "../parts/animations.js"
|
|
9
|
+
import {Crop, Effect, FilterableItem, Item, Kind, VisualAnimatableItem} from "../parts/item.js"
|
|
10
|
+
import {Anim, AnimateAction, Interpolation, Keyframes, TrackTransform, Transform, TransformOptions, Vec2, VisualAnimations} from "../types.js"
|
|
11
|
+
|
|
12
|
+
type VisualAnimateActions = {
|
|
13
|
+
[TKey in keyof VisualAnimations]-?: AnimateAction
|
|
14
|
+
}
|
|
10
15
|
|
|
11
16
|
export class O {
|
|
12
17
|
constructor(public state: {timeline: TimelineFile}) {}
|
|
@@ -59,6 +64,62 @@ export class O {
|
|
|
59
64
|
return item
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
animatedSpatial = (anim: Anim<TrackTransform>, crop?: Crop): Item.AnimatedSpatial => {
|
|
68
|
+
const item: Item.AnimatedSpatial = {
|
|
69
|
+
id: this.getId(),
|
|
70
|
+
kind: Kind.AnimatedSpatial,
|
|
71
|
+
anim,
|
|
72
|
+
crop,
|
|
73
|
+
enabled: true
|
|
74
|
+
}
|
|
75
|
+
this.register(item)
|
|
76
|
+
return item
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#registerAnimation = (anims: VisualAnimations): Item.Animation => {
|
|
80
|
+
const item: Item.Animation = {
|
|
81
|
+
id: this.getId(),
|
|
82
|
+
kind: Kind.Animation,
|
|
83
|
+
anims,
|
|
84
|
+
enabled: true
|
|
85
|
+
}
|
|
86
|
+
this.register(item)
|
|
87
|
+
return item
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
anim = {
|
|
91
|
+
scalar: (terp: Interpolation, track: Keyframes): Anim<Keyframes> => ({terp, track}),
|
|
92
|
+
|
|
93
|
+
vec2: (terp: Interpolation, source: Keyframes<Vec2>) => {
|
|
94
|
+
const track = {x: [] as Keyframes, y: [] as Keyframes}
|
|
95
|
+
|
|
96
|
+
for (const [time, [x, y]] of source) {
|
|
97
|
+
track.x.push([time, x])
|
|
98
|
+
track.y.push([time, y])
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {terp, track}
|
|
102
|
+
},
|
|
103
|
+
|
|
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
|
+
}
|
|
118
|
+
|
|
119
|
+
return {terp, track}
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
62
123
|
#makeFilter = <TFilter extends FilterType>(type: TFilter) => {
|
|
63
124
|
const make = (params?: FilterParams<TFilter>) => {
|
|
64
125
|
const item: Item.Filter<TFilter> = {
|
|
@@ -94,6 +155,38 @@ export class O {
|
|
|
94
155
|
|
|
95
156
|
filter = this.#makeFilters()
|
|
96
157
|
|
|
158
|
+
#makeAnimate = <TKey extends keyof VisualAnimations>(key: TKey): AnimateAction => {
|
|
159
|
+
const make = (terp: Interpolation, track: Keyframes) =>
|
|
160
|
+
this.#registerAnimation({
|
|
161
|
+
[key]: this.anim.scalar(terp, track)
|
|
162
|
+
} as Pick<VisualAnimations, TKey>)
|
|
163
|
+
|
|
164
|
+
const action = (<T extends VisualAnimatableItem>(
|
|
165
|
+
item: T,
|
|
166
|
+
terp: Interpolation,
|
|
167
|
+
track: Keyframes
|
|
168
|
+
): T => {
|
|
169
|
+
const animation = make(terp, track)
|
|
170
|
+
const next = {
|
|
171
|
+
...item,
|
|
172
|
+
animationId: animation.id
|
|
173
|
+
}
|
|
174
|
+
this.set<T>(item.id, next as Partial<T>)
|
|
175
|
+
return next
|
|
176
|
+
}) as AnimateAction
|
|
177
|
+
|
|
178
|
+
action.make = make
|
|
179
|
+
return action
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#makeAnimateActions = (): VisualAnimateActions => {
|
|
183
|
+
const entries = Object.keys(visualAnimations)
|
|
184
|
+
.map(key => [key, this.#makeAnimate(key as keyof VisualAnimations)])
|
|
185
|
+
return Object.fromEntries(entries) as VisualAnimateActions
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
animate = this.#makeAnimateActions()
|
|
189
|
+
|
|
97
190
|
sequence = (...items: Item.Any[]): Item.Sequence => {
|
|
98
191
|
const item = {
|
|
99
192
|
id: this.getId(),
|
package/s/timeline/types.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import {Item, VisualAnimatableItem} from "./parts/item.js"
|
|
2
|
+
|
|
3
|
+
export type Interpolation =
|
|
4
|
+
| "linear"
|
|
5
|
+
| "ease"
|
|
6
|
+
| "easeIn"
|
|
7
|
+
| "easeOut"
|
|
8
|
+
| "bounce"
|
|
9
|
+
| "catmullRom"
|
|
1
10
|
|
|
2
|
-
export type Interpolation = "linear" | "catmullRom"
|
|
3
11
|
export type Keyframe<Value = number> = [time: number, value: Value]
|
|
4
12
|
export type Keyframes<Value = number> = Keyframe<Value>[]
|
|
5
13
|
export type Vec2 = [x: number, y: number]
|
|
@@ -15,14 +23,29 @@ export type Anim<T> = {
|
|
|
15
23
|
track: T
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
export type Animations = Anim<TrackTransform>
|
|
19
|
-
|
|
20
26
|
export type TrackTransform = {
|
|
21
27
|
position: TrackVec2
|
|
22
28
|
scale: TrackVec2
|
|
23
29
|
rotation: Keyframes
|
|
24
30
|
}
|
|
25
31
|
|
|
32
|
+
export type ScalarAnimation = Anim<Keyframes>
|
|
33
|
+
export type Vec2Animation = Anim<TrackVec2>
|
|
34
|
+
export type TransformAnimation = Anim<TrackTransform>
|
|
35
|
+
export type VisualAnimations = {
|
|
36
|
+
opacity?: ScalarAnimation
|
|
37
|
+
}
|
|
38
|
+
export interface AnimateAction {
|
|
39
|
+
<T extends VisualAnimatableItem>(
|
|
40
|
+
item: T,
|
|
41
|
+
terp: Interpolation,
|
|
42
|
+
track: Keyframes
|
|
43
|
+
): T
|
|
44
|
+
make(terp: Interpolation, track: Keyframes): Item.Animation
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// export type Animations = Anim<TrackTransform>
|
|
48
|
+
|
|
26
49
|
export type TransformOptions = {
|
|
27
50
|
position?: Vec2
|
|
28
51
|
scale?: Vec2
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
import {resolveTerp} from "./terps.js"
|
|
3
|
+
import {Item, Kind} from "../parts/item.js"
|
|
4
|
+
import {Anim, Keyframes, ScalarAnimation, TrackTransform, Transform} from "../types.js"
|
|
5
|
+
|
|
6
|
+
const resolveScalar =(
|
|
7
|
+
time: number,
|
|
8
|
+
keys: Keyframes,
|
|
9
|
+
terp: Anim<Keyframes>["terp"],
|
|
10
|
+
): number => {
|
|
11
|
+
if (keys.length === 0)
|
|
12
|
+
return 0
|
|
13
|
+
|
|
14
|
+
if (keys.length === 1)
|
|
15
|
+
return keys[0][1]
|
|
16
|
+
|
|
17
|
+
const sorted = [...keys].sort((a, b) => a[0] - b[0])
|
|
18
|
+
|
|
19
|
+
if (time <= sorted[0][0])
|
|
20
|
+
return sorted[0][1]
|
|
21
|
+
|
|
22
|
+
const last = sorted[sorted.length - 1]
|
|
23
|
+
if (time >= last[0])
|
|
24
|
+
return last[1]
|
|
25
|
+
|
|
26
|
+
let index = 0
|
|
27
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
28
|
+
const a = sorted[i]
|
|
29
|
+
const b = sorted[i + 1]
|
|
30
|
+
if (time >= a[0] && time <= b[0]) {
|
|
31
|
+
index = i
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [t2] = sorted[index]
|
|
37
|
+
const [t3] = sorted[index + 1]
|
|
38
|
+
const span = t3 - t2
|
|
39
|
+
const x = span === 0 ? 0 : (time - t2) / span
|
|
40
|
+
|
|
41
|
+
return resolveTerp(terp, x, sorted, index)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const resolveTransformAnimation =(
|
|
45
|
+
time: number,
|
|
46
|
+
anim: Anim<TrackTransform>,
|
|
47
|
+
): Transform => ([
|
|
48
|
+
[
|
|
49
|
+
resolveScalar(time, anim.track.position.x, anim.terp),
|
|
50
|
+
resolveScalar(time, anim.track.position.y, anim.terp),
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
resolveScalar(time, anim.track.scale.x, anim.terp),
|
|
54
|
+
resolveScalar(time, anim.track.scale.y, anim.terp),
|
|
55
|
+
],
|
|
56
|
+
resolveScalar(time, anim.track.rotation, anim.terp),
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
export const resolveScalarAnimation =(
|
|
60
|
+
time: number,
|
|
61
|
+
anim: ScalarAnimation,
|
|
62
|
+
): number => resolveScalar(time, anim.track, anim.terp)
|
|
63
|
+
|
|
64
|
+
export const resolveSpatialTransform =(
|
|
65
|
+
spatial: Item.Spatial | Item.AnimatedSpatial,
|
|
66
|
+
time: number,
|
|
67
|
+
): Transform =>
|
|
68
|
+
spatial.kind === Kind.AnimatedSpatial
|
|
69
|
+
? resolveTransformAnimation(time, spatial.anim)
|
|
70
|
+
: spatial.transform
|
|
71
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
|
|
2
|
+
import {Interpolation, Keyframes} from "../types.js"
|
|
3
|
+
|
|
4
|
+
type EasingFn = (x: number, a: number, b: number) => number
|
|
5
|
+
|
|
6
|
+
class TerpKeys {
|
|
7
|
+
constructor(
|
|
8
|
+
private keys: Keyframes,
|
|
9
|
+
private index: number,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
near(offset: number): number {
|
|
13
|
+
const next = this.keys[Math.max(0, Math.min(this.keys.length - 1, this.index + offset))]
|
|
14
|
+
return next[1]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type TerpFn = (x: number, keys: TerpKeys) => number
|
|
19
|
+
|
|
20
|
+
const asTerp = (fn: TerpFn): TerpFn => fn
|
|
21
|
+
|
|
22
|
+
const fromEasingFn = (easing: EasingFn): TerpFn =>
|
|
23
|
+
asTerp((x, keys) => easing(x, keys.near(0), keys.near(1)))
|
|
24
|
+
|
|
25
|
+
const lerp = (x: number, a: number, b: number) => a + (b - a) * x
|
|
26
|
+
|
|
27
|
+
export const terps: Record<Interpolation, TerpFn> = {
|
|
28
|
+
linear: fromEasingFn(lerp),
|
|
29
|
+
|
|
30
|
+
ease: fromEasingFn((x, a, b) => {
|
|
31
|
+
const eased = x < 0.5
|
|
32
|
+
? 4 * x * x * x
|
|
33
|
+
: 1 - Math.pow(-2 * x + 2, 3) / 2
|
|
34
|
+
return lerp(eased, a, b)
|
|
35
|
+
}),
|
|
36
|
+
|
|
37
|
+
easeIn: fromEasingFn((x, a, b) => lerp(x * x * x, a, b)),
|
|
38
|
+
|
|
39
|
+
easeOut: fromEasingFn((x, a, b) => lerp(1 - Math.pow(1 - x, 3), a, b)),
|
|
40
|
+
|
|
41
|
+
bounce: fromEasingFn((x, a, b) => {
|
|
42
|
+
const n1 = 7.5625
|
|
43
|
+
const d1 = 2.75
|
|
44
|
+
const eased = x < 1 / d1
|
|
45
|
+
? n1 * x * x
|
|
46
|
+
: x < 2 / d1
|
|
47
|
+
? n1 * (x - 1.5 / d1) ** 2 + 0.75
|
|
48
|
+
: x < 2.5 / d1
|
|
49
|
+
? n1 * (x - 2.25 / d1) ** 2 + 0.9375
|
|
50
|
+
: n1 * (x - 2.625 / d1) ** 2 + 0.984375
|
|
51
|
+
return lerp(eased, a, b)
|
|
52
|
+
}),
|
|
53
|
+
|
|
54
|
+
catmullRom: asTerp((x, keys) => {
|
|
55
|
+
const p1 = keys.near(-1)
|
|
56
|
+
const p2 = keys.near(0)
|
|
57
|
+
const p3 = keys.near(1)
|
|
58
|
+
const p4 = keys.near(2)
|
|
59
|
+
const x2 = x * x
|
|
60
|
+
const x3 = x2 * x
|
|
61
|
+
return 0.5 * (
|
|
62
|
+
(2 * p2) +
|
|
63
|
+
(-p1 + p3) * x +
|
|
64
|
+
(2 * p1 - 5 * p2 + 4 * p3 - p4) * x2 +
|
|
65
|
+
(-p1 + 3 * p2 - 3 * p3 + p4) * x3
|
|
66
|
+
)
|
|
67
|
+
}),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const resolveTerp =(
|
|
71
|
+
terp: Interpolation,
|
|
72
|
+
x: number,
|
|
73
|
+
keys: Keyframes,
|
|
74
|
+
index: number,
|
|
75
|
+
): number => {
|
|
76
|
+
const fn = terps[terp]
|
|
77
|
+
if (!fn)
|
|
78
|
+
throw new Error(`unknown terp "${terp}"`)
|
|
79
|
+
|
|
80
|
+
return fn(x, new TerpKeys(keys, index))
|
|
81
|
+
}
|