type Vec2 = [number, number]; type Rad = number; interface Graphics { clear(): void; drawCar(pos: Vec2, angle: Rad): void; } class CanvasGraphics implements Graphics { private canvas = document.querySelector("#canvas")!; private ctx = this.canvas.getContext("2d")!; private alpha = 1; private trans: Vec2 = [100, 350]; public setup() { this.canvas.width = 500; this.canvas.height = 500; } public clear() { this.setFill(100, 170, 255); this.fillRect([0, 0], [this.canvas.width, this.canvas.height]); // Draw ground this.setFill(50, 155, 50); this.fillRect([0, 350], [this.canvas.width, 150]); } public drawCar(pos: Vec2, angle: Rad) { const [x, y] = pos; this.ctx.save(); this.ctx.translate(...this.trans); this.ctx.save(); this.ctx.translate(60 + x, 0 + y); this.ctx.rotate(angle); // Draw car body this.setFill(200, 0, 0); this.fillRect([-20, -40], [80, 21]); this.fillRect([-20, -20], [100, 20]); // Draw rear wheel this.setFill(0, 0, 0); this.fillCircle([0, 0], 10); // Draw front wheel this.fillCircle([60, 0], 10); this.ctx.restore(); this.ctx.restore(); } private setFill(red: number, green: number, blue: number): void { this.ctx.fillStyle = `rgba(${red}, ${green}, ${blue}, ${this.alpha})`; } private fillRect(pos: Vec2, size: Vec2): void { const [x, y] = pos; const [width, height] = size; this.ctx.fillRect(x, y, width, height); } private fillCircle(pos: Vec2, radius: number) { const [x, y] = pos; this.ctx.beginPath(); this.ctx.arc(x, y, radius, 0, Math.PI * 2); this.ctx.fill(); } } interface Entity { render(graphics: Graphics): void; update(delta: number): void; } class Car implements Entity { private pos: Vec2 = [0, 0]; private angle: Rad = 0; private speeder = 1.0; public render(graphics: Graphics): void { graphics.drawCar(this.pos, this.angle); } public update(delta: number): void { if (this.angle > Math.PI / -2) { this.angle += this.speeder * -0.005 * Math.PI; this.pos[0] += this.speeder * delta * 100; } } public setSpeeder(speeder: number): void { this.speeder = speeder; } public getAngle(): number { return this.angle; } } class Matrix { private entities: Entity[] = [] private graphics = new CanvasGraphics(); public setup() { this.graphics.setup(); } public start() { const looped = (before: number) => { const now = Date.now(); const delta = (now - before) / 1000; for (const entity of this.entities) { entity.update(delta); } this.graphics.clear(); for (const entity of this.entities) { entity.render(this.graphics); } requestAnimationFrame(() => looped(now)); } looped(Date.now()); } public addEntity(entity: Entity) { this.entities.push(entity); } } function main() { const matricen = new Matrix(); matricen.setup(); const car = new Car(); matricen.addEntity(car); matricen.start(); } main();