Basic animation timeline
This commit is contained in:
parent
a9f7e2c470
commit
9564299a26
53
src/main.ts
53
src/main.ts
@ -10,6 +10,7 @@ import { createBasicVao } from "./utils/vao";
|
||||
import Shape2D from "./geometry/Shape2D";
|
||||
|
||||
import outline from "./data/letter_outline.json";
|
||||
import { Timeline, Track } from "./utils/sequence";
|
||||
|
||||
function render() {
|
||||
const canvas = document.querySelector<HTMLCanvasElement>("#canvas");
|
||||
@ -45,11 +46,39 @@ function render() {
|
||||
const aVao = createBasicVao(gl, aBuffer, vertexPositionAttribLocation);
|
||||
|
||||
const shapes: Shape2D[] = [
|
||||
new Shape2D(new Vector2D(300, canvas.height/2), 200, [0.22745, 0.35294, 0.25098], kVerts.length / 2, kVao, gl.LINES),
|
||||
new Shape2D(new Vector2D(600, canvas.height/2), 200, [0.22745, 0.35294, 0.25098], eVerts.length / 2, eVao, gl.LINES),
|
||||
new Shape2D(new Vector2D(900, canvas.height/2), 200, [0.22745, 0.35294, 0.25098], aVerts.length / 2, aVao, gl.LINES),
|
||||
new Shape2D(new Vector2D(0, 0), 200, [0.22745, 0.35294, 0.25098], kVerts.length / 2, kVao, gl.LINES),
|
||||
new Shape2D(new Vector2D(0, 0), 200, [0.22745, 0.35294, 0.25098], eVerts.length / 2, eVao, gl.LINES),
|
||||
new Shape2D(new Vector2D(0, 0), 200, [0.22745, 0.35294, 0.25098], aVerts.length / 2, aVao, gl.LINES),
|
||||
]
|
||||
|
||||
const pTrackK = new Track([
|
||||
{ time: 0, value: [300, -200] },
|
||||
{ time: 1, value: [300, 300] },
|
||||
]);
|
||||
|
||||
const pTrackE = new Track([
|
||||
{ time: 1, value: [600, 900] },
|
||||
{ time: 2, value: [600, 300] },
|
||||
]);
|
||||
|
||||
const pTrackA = new Track([
|
||||
{ time: 2, value: [900, -200] },
|
||||
{ time: 3, value: [900, 300] },
|
||||
]);
|
||||
|
||||
const bgTrack = new Track([
|
||||
{ time: 0, value: [0.1, 0.1, 0.1, 1.0] },
|
||||
{ time: 3, value: [0.1, 0.1, 0.1, 1.0] },
|
||||
{ time: 4, value: [0.2, 0.9, 0.2, 1.0] },
|
||||
]);
|
||||
|
||||
const timeline = new Timeline({
|
||||
"positionK": pTrackK,
|
||||
"positionE": pTrackE,
|
||||
"positionA": pTrackA,
|
||||
"backgroundColor": bgTrack
|
||||
}, 4);
|
||||
|
||||
const fragmentColorUniform = gl.getUniformLocation(program, "uColor");
|
||||
const canvasSizeUniform = gl.getUniformLocation(program, "uCanvasSize");
|
||||
const shapeLocationUniform = gl.getUniformLocation(program, "uLocation");
|
||||
@ -63,23 +92,35 @@ function render() {
|
||||
`);
|
||||
}
|
||||
|
||||
let startTime = performance.now();
|
||||
|
||||
const frame = () => {
|
||||
const currentTime = (performance.now() - startTime) / 1000;
|
||||
const interpolatedValues = timeline.update(currentTime);
|
||||
|
||||
const [r, g, b, a] = interpolatedValues.backgroundColor as number[];
|
||||
|
||||
canvas.width = canvas.clientWidth;
|
||||
canvas.height = canvas.clientHeight;
|
||||
gl.clearColor(0.1, 0.1, 0.1, 1.0);
|
||||
gl.clearColor(r, g, b, a);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
gl.uniform2f(canvasSizeUniform, canvas.width, canvas.height);
|
||||
|
||||
shapes.forEach((shape) => {
|
||||
shapes.forEach((shape, index) => {
|
||||
const positionKey = `position${"KEA"[index]}`;
|
||||
const [x, y] = interpolatedValues[positionKey] as number[];
|
||||
|
||||
gl.uniform3f(fragmentColorUniform, shape.color[0], shape.color[1], shape.color[2]);
|
||||
gl.uniform2f(shapeLocationUniform, shape.position.x, shape.position.y);
|
||||
gl.uniform2f(shapeLocationUniform, x, y);
|
||||
gl.uniform1f(shapeScaleUniform, shape.scale);
|
||||
|
||||
gl.bindVertexArray(shape.vao);
|
||||
gl.drawArrays(shape.renderType, 0, shape.verticesCount);
|
||||
});
|
||||
|
||||
requestAnimationFrame(frame)
|
||||
}
|
||||
|
||||
requestAnimationFrame(frame);
|
||||
|
||||
62
src/utils/sequence.ts
Normal file
62
src/utils/sequence.ts
Normal file
@ -0,0 +1,62 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user