62 lines
1.6 KiB
TypeScript
62 lines
1.6 KiB
TypeScript
export type InterpolatableValue = number[] | number;
|
|
|
|
export interface Keyframe {
|
|
time: number;
|
|
value: InterpolatableValue;
|
|
}
|
|
|
|
export class Track {
|
|
keyframes: Keyframe[];
|
|
constructor(keyframes: Keyframe[]) {
|
|
this.keyframes = keyframes.sort((a, b) => a.time - b.time);
|
|
}
|
|
|
|
interpolate(time: number): InterpolatableValue {
|
|
if (time <= this.keyframes[0].time) {
|
|
return this.keyframes[0].value;
|
|
}
|
|
|
|
if (time >= this.keyframes[this.keyframes.length - 1].time) {
|
|
return this.keyframes[this.keyframes.length - 1].value;
|
|
}
|
|
|
|
let prevIndex = 0;
|
|
for (let i = 1; i < this.keyframes.length; i++) {
|
|
if (time < this.keyframes[i].time) {
|
|
break;
|
|
}
|
|
prevIndex = i;
|
|
}
|
|
|
|
const prev = this.keyframes[prevIndex];
|
|
const next = this.keyframes[prevIndex + 1];
|
|
|
|
const t = (time - prev.time) / (next.time - prev.time);
|
|
|
|
if (Array.isArray(prev.value)) {
|
|
return prev.value.map((v, i) => v + t * ((next.value as number[])[i] - v));
|
|
} else {
|
|
return prev.value + t * ((next.value as number) - prev.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
export class Timeline {
|
|
tracks: { [key: string]: Track };
|
|
duration: number;
|
|
|
|
constructor(tracks: { [key: string]: Track }, duration: number) {
|
|
this.tracks = tracks;
|
|
this.duration = duration;
|
|
}
|
|
|
|
update(time: number): { [key: string]: InterpolatableValue } {
|
|
const result: { [key: string]: InterpolatableValue } = {};
|
|
|
|
for (const key in this.tracks) {
|
|
result[key] = this.tracks[key].interpolate(Math.min(time, this.duration));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
} |