diff --git a/move.html b/move.html
index 83c6e2c..6d21d8f 100644
--- a/move.html
+++ b/move.html
@@ -4,8 +4,11 @@
Hello typescript
+
-
+
diff --git a/move.ts b/move.ts
index cba0444..9c77de7 100644
--- a/move.ts
+++ b/move.ts
@@ -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();
diff --git a/vec.ts b/vec.ts
index 042d7d7..cb1dee8 100644
--- a/vec.ts
+++ b/vec.ts
@@ -13,6 +13,10 @@ export default class Vec2d implements Point {
this.y = y;
}
+ static fromPolar(r: number, theta: number) {
+ return new Vec2d(r * Math.cos(theta), r * Math.sin(theta));
+ }
+
static random(maxX: number, maxY: number) {
return new Vec2d(Math.random() * maxX, Math.random() * maxY);
}