move: implement circle movement
This commit is contained in:
@@ -4,7 +4,9 @@ import { drawCircle, resizeCanvas } from "./common.js";
|
||||
const SEGMENT_SPACING = 100;
|
||||
|
||||
function initTrail(pos: Vec2d, len: number) {
|
||||
return Array.from({ length: len }, (_, i) => pos.add(new Vec2d(0, -1).scale(i * SEGMENT_SPACING)));
|
||||
return Array.from({ length: len }, (_, i) =>
|
||||
pos.add(new Vec2d(0, -1).scale(i * SEGMENT_SPACING)),
|
||||
);
|
||||
}
|
||||
|
||||
// state
|
||||
@@ -12,19 +14,35 @@ let target: Vec2d | undefined = undefined;
|
||||
let trail = initTrail(new Vec2d(200, 200), 5);
|
||||
let velocity = new Vec2d(500, 500);
|
||||
let pause = false;
|
||||
let mode: "follow" | "bounce" = "bounce";
|
||||
let mode: "follow" | "bounce" | "circle" = "follow";
|
||||
let lastFrameTime: number | undefined = undefined;
|
||||
|
||||
function update(ctx: CanvasRenderingContext2D, timestamp: number) {
|
||||
if (!lastFrameTime) lastFrameTime = timestamp;
|
||||
const dt = (timestamp - lastFrameTime) / 1000; // seconds
|
||||
lastFrameTime = timestamp;
|
||||
if (!pause) {
|
||||
switch (mode) {
|
||||
case "bounce": updateBounce(ctx, dt); break;
|
||||
case "follow": updateFollow(ctx, dt); break;
|
||||
default: throw new Error(`Unknown mode: ${mode}`);
|
||||
case "bounce":
|
||||
updateBounce(ctx, dt);
|
||||
break;
|
||||
case "follow":
|
||||
updateFollow(ctx, dt);
|
||||
break;
|
||||
case "circle":
|
||||
updateCircle(ctx, dt);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown mode: ${mode}`);
|
||||
}
|
||||
if (!pause) window.requestAnimationFrame(t => update(ctx, t));
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
for (const pos of trail) {
|
||||
drawCircle(ctx, pos, 20, 0xff00ff);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame((t) => update(ctx, t));
|
||||
}
|
||||
|
||||
function updateFollow(ctx: CanvasRenderingContext2D, dt: number) {
|
||||
@@ -34,32 +52,43 @@ function updateFollow(ctx: CanvasRenderingContext2D, dt: number) {
|
||||
// frame, so this is framerate dependent:
|
||||
// trail[0] = trail[0].lerp(target, 0.1);
|
||||
// use exponential decay instead, this is framerate independent:
|
||||
const alpha = Math.exp(-10 * dt)
|
||||
const alpha = Math.exp(-10 * dt);
|
||||
trail[0] = trail[0].lerp(target, alpha);
|
||||
updateTrail();
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
for (const pos of trail) {
|
||||
drawCircle(ctx, pos, 20, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
|
||||
function updateBounce(ctx: CanvasRenderingContext2D, dt: number) {
|
||||
// update head pos
|
||||
// P_t+1 = P_t + V * t
|
||||
const newPos = trail[0].add(velocity.scale(dt));
|
||||
if (newPos.x > ctx.canvas.width - 100) { velocity.x *= -1; newPos.x = ctx.canvas.width - 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;
|
||||
updateTrail();
|
||||
// update head pos
|
||||
// P_t+1 = P_t + V * t
|
||||
const newPos = trail[0].add(velocity.scale(dt));
|
||||
if (newPos.x > ctx.canvas.width - 100) {
|
||||
velocity.x *= -1;
|
||||
newPos.x = ctx.canvas.width - 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;
|
||||
updateTrail();
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
for (const pos of trail) {
|
||||
drawCircle(ctx, pos, 20, 0xFF00FF);
|
||||
}
|
||||
function updateCircle(ctx: CanvasRenderingContext2D, dt: number) {
|
||||
const center = new Vec2d(ctx.canvas.width / 2, ctx.canvas.height / 2);
|
||||
const rel = trail[0].sub(center);
|
||||
const radius = rel.length();
|
||||
const theta = Math.atan2(rel.y, rel.x) + dt;
|
||||
trail[0] = center.add(Vec2d.fromPolar(radius, theta));
|
||||
updateTrail();
|
||||
}
|
||||
|
||||
function updateTrail() {
|
||||
@@ -78,33 +107,32 @@ function updateTrail() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement | null;
|
||||
if (!canvas) throw new Error("unable to get canvas HTML element");
|
||||
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D | null;
|
||||
if (!ctx) throw new Error("unable to get canvas 2D context");
|
||||
ctx.canvas.width = window.innerWidth
|
||||
ctx.canvas.height = window.innerHeight
|
||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement | null;
|
||||
if (!canvas) throw new Error("unable to get canvas HTML element");
|
||||
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D | null;
|
||||
if (!ctx) throw new Error("unable to get canvas 2D context");
|
||||
ctx.canvas.width = ctx.canvas.clientWidth;
|
||||
ctx.canvas.height = ctx.canvas.clientHeight;
|
||||
|
||||
canvas.onmousemove = (evt) => {
|
||||
const {clientX, clientY} = evt;
|
||||
target = new Vec2d(clientX, clientY);
|
||||
}
|
||||
canvas.onmousemove = (evt) => {
|
||||
const { clientX, clientY } = evt;
|
||||
target = new Vec2d(clientX, clientY);
|
||||
};
|
||||
|
||||
canvas.onclick = () => {
|
||||
if (pause) {
|
||||
window.requestAnimationFrame(t => update(ctx, t));
|
||||
}
|
||||
pause = !pause;
|
||||
}
|
||||
canvas.onclick = () => {
|
||||
pause = !pause;
|
||||
};
|
||||
|
||||
window.onkeydown = (evt) => {
|
||||
if (mode === "follow") mode = "bounce";
|
||||
else mode = "follow"
|
||||
}
|
||||
window.onkeydown = (evt) => {
|
||||
if (mode === "bounce") mode = "follow";
|
||||
else if (mode === "follow") mode = "circle";
|
||||
else if (mode === "circle") mode = "bounce";
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user