diff --git a/index.html b/index.html index 645a5e2..b86000a 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,7 @@
  • Bezier demo #2
  • Bezier spline demo
  • Image demo
  • +
  • Move demo
  • diff --git a/move.html b/move.html new file mode 100644 index 0000000..83c6e2c --- /dev/null +++ b/move.html @@ -0,0 +1,12 @@ + + + + + + Hello typescript + + + + + + diff --git a/move.ts b/move.ts new file mode 100644 index 0000000..68ba945 --- /dev/null +++ b/move.ts @@ -0,0 +1,123 @@ +class Vec2d { + x: number; + y: number; + + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + + toString() { + return `[${this.x}, ${this.y}]`; + } + + scale(scalar: number): Vec2d { + return new Vec2d(this.x * scalar, this.y * scalar); + } + + add(other: Vec2d): Vec2d { + return new Vec2d(this.x + other.x, this.y + other.y) + } + + lerp(other: Vec2d, t: number): Vec2d { + // (1-t)*A + B*t + return this.scale(1-t).add(other.scale(t)); + } +} + +// state +let target: Vec2d | undefined = undefined; +let pos = new Vec2d(200, 200); +let velocity = new Vec2d(500, 500); +let pause = false; +let mode: "follow" | "bounce" = "bounce"; +let start: number | undefined = undefined; + +function drawCircle(ctx: CanvasRenderingContext2D, center: Vec2d, radius: number, color: number) { + ctx.save(); + ctx.beginPath(); + ctx.arc(center.x, center.y, radius, 0, 2*Math.PI); + ctx.strokeStyle = `#${color.toString(16)}` + ctx.lineWidth = 5; + ctx.stroke(); + ctx.restore(); +} + +function resizeCanvas(ctx: CanvasRenderingContext2D) { + ctx.canvas.width = window.innerWidth; + ctx.canvas.height = window.innerHeight; + ctx.clearRect(0, 0, ctx.canvas.height, ctx. canvas.width); +} + +function update(ctx: CanvasRenderingContext2D, timestamp: number) { + switch (mode) { + case "bounce": + updateBounce(ctx, timestamp); + break; + case "follow": + updateFollow(ctx); + break; + default: + throw new Error(`Unknown mode: ${mode}`); + } + if (!pause) window.requestAnimationFrame(t => update(ctx, t)); +} + +function updateFollow(ctx: CanvasRenderingContext2D) { + if (target) pos = pos.lerp(target, 0.01); + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + drawCircle(ctx, pos, 100, 0xFF00FF); +} + +function updateBounce(ctx: CanvasRenderingContext2D, timestamp: number) { + if (!start) start = timestamp; + const dt = timestamp - start; + // P_t+1 = P_t + V * t + const newPos = pos.add(velocity.scale(0.001*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; } + //console.log(velocity); + + pos = newPos; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + drawCircle(ctx, pos, 100, 0xFF00FF); + start = timestamp; +} + +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; + + + canvas.onmousemove = (evt) => { + const {clientX, clientY} = evt; + target = new Vec2d(clientX, clientY); + } + + canvas.onclick = () => { + if (pause) { + window.requestAnimationFrame(t => update(ctx, t)); + } + pause = !pause; + } + + window.onkeydown = (evt) => { + console.log("key down", evt); + if (mode === "follow") mode = "bounce"; + else mode = "follow" + console.log("mode", mode); + } + + window.addEventListener('resize', () => resizeCanvas(ctx)); + + window.requestAnimationFrame(t => update(ctx, t)); +} + +init();