Refactor animator + allow loop
This commit is contained in:
parent
9564299a26
commit
a611b6c72d
10
src/main.ts
10
src/main.ts
@ -73,11 +73,11 @@ function render() {
|
||||
]);
|
||||
|
||||
const timeline = new Timeline({
|
||||
"positionK": pTrackK,
|
||||
"positionE": pTrackE,
|
||||
"positionA": pTrackA,
|
||||
"position0": pTrackK,
|
||||
"position1": pTrackE,
|
||||
"position2": pTrackA,
|
||||
"backgroundColor": bgTrack
|
||||
}, 4);
|
||||
}, 10, true);
|
||||
|
||||
const fragmentColorUniform = gl.getUniformLocation(program, "uColor");
|
||||
const canvasSizeUniform = gl.getUniformLocation(program, "uCanvasSize");
|
||||
@ -109,7 +109,7 @@ function render() {
|
||||
gl.uniform2f(canvasSizeUniform, canvas.width, canvas.height);
|
||||
|
||||
shapes.forEach((shape, index) => {
|
||||
const positionKey = `position${"KEA"[index]}`;
|
||||
const positionKey = `position${index}`;
|
||||
const [x, y] = interpolatedValues[positionKey] as number[];
|
||||
|
||||
gl.uniform3f(fragmentColorUniform, shape.color[0], shape.color[1], shape.color[2]);
|
||||
|
||||
@ -1,62 +1,70 @@
|
||||
export type InterpolatableValue = number[] | number;
|
||||
export type InterpolatableValue = number | number[];
|
||||
|
||||
export interface Keyframe {
|
||||
interface Keyframe {
|
||||
time: number;
|
||||
value: InterpolatableValue;
|
||||
}
|
||||
|
||||
export class Track {
|
||||
keyframes: Keyframe[];
|
||||
private readonly 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;
|
||||
}
|
||||
const { keyframes } = this;
|
||||
const lastIndex = keyframes.length - 1;
|
||||
|
||||
let prevIndex = 0;
|
||||
for (let i = 1; i < this.keyframes.length; i++) {
|
||||
if (time < this.keyframes[i].time) {
|
||||
break;
|
||||
}
|
||||
prevIndex = i;
|
||||
if (time <= keyframes[0].time) return keyframes[0].value;
|
||||
if (time >= keyframes[lastIndex].time) return keyframes[lastIndex].value;
|
||||
|
||||
const nextIndex = keyframes.findIndex(kf => kf.time > time);
|
||||
const prevIndex = nextIndex - 1;
|
||||
|
||||
const [prevKf, nextKf] = [keyframes[prevIndex], keyframes[nextIndex]];
|
||||
const t = (time - prevKf.time) / (nextKf.time - prevKf.time);
|
||||
|
||||
return this.interpolateValues(prevKf.value, nextKf.value, t);
|
||||
}
|
||||
|
||||
private interpolateValues(start: InterpolatableValue, end: InterpolatableValue, t: number): InterpolatableValue {
|
||||
if (Array.isArray(start) && Array.isArray(end)) {
|
||||
return start.map((v, i) => v + t * (end[i] - v));
|
||||
}
|
||||
|
||||
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);
|
||||
if (typeof start === 'number' && typeof end === 'number') {
|
||||
return start + t * (end - start);
|
||||
}
|
||||
throw new Error('Mismatched value types in keyframes');
|
||||
}
|
||||
}
|
||||
|
||||
export class Timeline {
|
||||
tracks: { [key: string]: Track };
|
||||
duration: number;
|
||||
private readonly tracks: Record<string, Track>;
|
||||
private readonly duration: number;
|
||||
private loop: boolean;
|
||||
|
||||
constructor(tracks: { [key: string]: Track }, duration: number) {
|
||||
constructor(tracks: Record<string, Track>, duration: number, loop: boolean = false) {
|
||||
this.tracks = tracks;
|
||||
this.duration = duration;
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
update(time: number): { [key: string]: InterpolatableValue } {
|
||||
const result: { [key: string]: InterpolatableValue } = {};
|
||||
setLoop(loop: boolean): void {
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
for (const key in this.tracks) {
|
||||
result[key] = this.tracks[key].interpolate(Math.min(time, this.duration));
|
||||
update(time: number): Record<string, InterpolatableValue> {
|
||||
let adjustedTime = time;
|
||||
|
||||
if (this.loop) {
|
||||
adjustedTime = time % this.duration;
|
||||
} else {
|
||||
adjustedTime = Math.min(time, this.duration);
|
||||
}
|
||||
|
||||
return result;
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.tracks).map(([key, track]) => [key, track.interpolate(adjustedTime)])
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user