reformat move.ts

This commit is contained in:
2026-08-06 14:07:33 +02:00
parent 0136981551
commit 6f43c64349
2 changed files with 50 additions and 63 deletions
+24 -44
View File
@@ -4,14 +4,12 @@ import { drawCircle, resizeCanvas } from "./common.js";
const SEGMENT_SPACING = 100; const SEGMENT_SPACING = 100;
function initTrail(pos: Vec2d, len: number) { function initTrail(pos: Vec2d, len: number) {
return Array.from({ length: len }, (_, i) => return Array.from({ length: len }, (_, i) => pos.add(new Vec2d(-1, 0).scale(i * SEGMENT_SPACING)));
pos.add(new Vec2d(0, -1).scale(i * SEGMENT_SPACING)),
);
} }
// state // state
let target: Vec2d | undefined = undefined; let target: Vec2d | undefined = undefined;
let trail = initTrail(new Vec2d(200, 200), 5); let trail = initTrail(new Vec2d(500, 500), 5);
let velocity = new Vec2d(500, 500); let velocity = new Vec2d(500, 500);
let pause = false; let pause = false;
let mode: "follow" | "bounce" | "circle" = "follow"; let mode: "follow" | "bounce" | "circle" = "follow";
@@ -23,26 +21,19 @@ function update(ctx: CanvasRenderingContext2D, timestamp: number) {
lastFrameTime = timestamp; lastFrameTime = timestamp;
if (!pause) { if (!pause) {
switch (mode) { switch (mode) {
case "bounce": case "bounce": updateBounce(ctx, dt); break;
updateBounce(ctx, dt); case "follow": updateFollow(ctx, dt); break;
break; case "circle": updateCircle(ctx, dt); break;
case "follow": default: throw new Error(`Unknown mode: ${mode}`);
updateFollow(ctx, dt);
break;
case "circle":
updateCircle(ctx, dt);
break;
default:
throw new Error(`Unknown mode: ${mode}`);
} }
} }
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
for (const pos of trail) { for (const pos of trail) {
drawCircle(ctx, pos, 20, 0xff00ff); drawCircle(ctx, pos, 20, 0xFF00FF);
} }
window.requestAnimationFrame((t) => update(ctx, t)); window.requestAnimationFrame(t => update(ctx, t));
} }
function updateFollow(ctx: CanvasRenderingContext2D, dt: number) { function updateFollow(ctx: CanvasRenderingContext2D, dt: number) {
@@ -52,7 +43,9 @@ function updateFollow(ctx: CanvasRenderingContext2D, dt: number) {
// frame, so this is framerate dependent: // frame, so this is framerate dependent:
// trail[0] = trail[0].lerp(target, 0.1); // trail[0] = trail[0].lerp(target, 0.1);
// use exponential decay instead, this is framerate independent: // use exponential decay instead, this is framerate independent:
const alpha = Math.exp(-10 * dt); // a = a + (b - a) * t lerp formula
// a = a + (b - a) * e^(-k * dt) substitute t with e^(-k * dt)
const alpha = Math.exp(-10 * dt)
trail[0] = trail[0].lerp(target, alpha); trail[0] = trail[0].lerp(target, alpha);
updateTrail(); updateTrail();
} }
@@ -62,32 +55,19 @@ function updateBounce(ctx: CanvasRenderingContext2D, dt: number) {
// update head pos // update head pos
// P_t+1 = P_t + V * t // P_t+1 = P_t + V * t
const newPos = trail[0].add(velocity.scale(dt)); const newPos = trail[0].add(velocity.scale(dt));
if (newPos.x > ctx.canvas.width - 100) { if (newPos.x > ctx.canvas.width - 100) { velocity.x *= -1; newPos.x = ctx.canvas.width - 100; }
velocity.x *= -1; if (newPos.y > ctx.canvas.height - 100) { velocity.y *= -1; newPos.y = ctx.canvas.height - 100; }
newPos.x = ctx.canvas.width - 100; if (newPos.x < 100) { velocity.x *= -1; newPos.x = 100; }
} if (newPos.y < 100) { velocity.y *= -1; newPos.y = 100; }
if (newPos.y > ctx.canvas.height - 100) {
velocity.y *= -1;
newPos.y = ctx.canvas.height - 100;
}
if (newPos.x < 100) {
velocity.x *= -1;
newPos.x = 100;
}
if (newPos.y < 100) {
velocity.y *= -1;
newPos.y = 100;
}
trail[0] = newPos; trail[0] = newPos;
updateTrail(); updateTrail();
} }
function updateCircle(ctx: CanvasRenderingContext2D, dt: number) { function updateCircle(ctx: CanvasRenderingContext2D, dt: number) {
const center = new Vec2d(ctx.canvas.width / 2, ctx.canvas.height / 2); const center = new Vec2d(ctx.canvas.width / 2, ctx.canvas.height / 2);
const rel = trail[0].sub(center); const rel = trail[0].sub(center); // distance from center
const radius = rel.length(); const { r, theta } = rel.toPolar();
const theta = Math.atan2(rel.y, rel.x) + dt; trail[0] = center.add(Vec2d.fromPolar(r, theta + dt));
trail[0] = center.add(Vec2d.fromPolar(radius, theta));
updateTrail(); updateTrail();
} }
@@ -115,24 +95,24 @@ function init() {
ctx.canvas.height = ctx.canvas.clientHeight; ctx.canvas.height = ctx.canvas.clientHeight;
canvas.onmousemove = (evt) => { canvas.onmousemove = (evt) => {
const { clientX, clientY } = evt; const {clientX, clientY} = evt;
target = new Vec2d(clientX, clientY); target = new Vec2d(clientX, clientY);
}; }
canvas.onclick = () => { canvas.onclick = () => {
pause = !pause; pause = !pause;
}; }
window.onkeydown = (evt) => { window.onkeydown = (evt) => {
if (mode === "bounce") mode = "follow"; if (mode === "bounce") mode = "follow";
else if (mode === "follow") mode = "circle"; else if (mode === "follow") mode = "circle";
else if (mode === "circle") mode = "bounce"; else if (mode === "circle") mode = "bounce";
console.log(mode); console.log(mode);
}; }
window.addEventListener("resize", () => resizeCanvas(ctx)); window.addEventListener('resize', () => resizeCanvas(ctx));
window.requestAnimationFrame((t) => update(ctx, t)); window.requestAnimationFrame(t => update(ctx, t));
} }
init(); init();
+7
View File
@@ -25,6 +25,13 @@ export default class Vec2d implements Point {
return `[${this.x}, ${this.y}]`; return `[${this.x}, ${this.y}]`;
} }
toPolar() {
return {
r: this.length(),
theta: Math.atan2(this.y, this.x),
};
}
normalize(): Vec2d { normalize(): Vec2d {
const len = this.length(); const len = this.length();
return new Vec2d(this.x / len, this.y / len); return new Vec2d(this.x / len, this.y / len);