1
0
Fork 0

Start creating zombies

This commit is contained in:
Pabloader 2025-06-25 13:39:32 +00:00
parent 63760d4cb8
commit efba557e96
2 changed files with 165 additions and 0 deletions

View File

@ -0,0 +1,49 @@
import { createCanvas } from "@common/display/canvas";
import Spinner from "./spinner";
const canvas = createCanvas(1024, 1024);
const spinner = new Spinner();
async function update(dt: number) {
spinner.update(dt);
}
async function render(ctx: CanvasRenderingContext2D) {
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(100, 100);
ctx.scale(200, 200);
spinner.render(ctx);
ctx.restore();
}
export default async function main() {
document.body.style.background = 'green';
canvas.style.imageRendering = 'auto';
const ctx = canvas.getContext('2d');
spinner.addListener(console.log);
canvas.addEventListener('click', () => {
spinner.spin();
})
if (ctx) {
let prevFrame = performance.now();
const loop = () => {
const now = performance.now()
const dt = (now - prevFrame) / 1000;
prevFrame = now;
update(dt);
render(ctx);
requestAnimationFrame(loop);
};
loop();
}
}

View File

@ -0,0 +1,116 @@
export type SpinnerListener = (angle: number) => void;
export default class Spinner {
private readonly probabilities = [0.3, 0.3, 0.2, 0.2];
private readonly colors = ['yellow', 'green', 'blue', 'red'];
private readonly startAngle = -Math.PI / 2 - this.probabilities[0] * 2 * Math.PI;
private angle = this.startAngle;
private speed = 0;
private friction = 0.3;
private fired = true;
private listeners = new Set<SpinnerListener>();
public render(ctx: CanvasRenderingContext2D) {
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(0.5, 0.5, 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.lineCap = 'butt';
ctx.fillStyle = 'black';
ctx.font = '0.1px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.lineWidth = 0.2;
let angle = this.startAngle;
for (let i = 0; i < this.probabilities.length; i++) {
ctx.strokeStyle = this.colors[i];
ctx.beginPath();
const nextAngle = angle + this.probabilities[i] * 2 * Math.PI;
ctx.arc(0.5, 0.5, 0.4, angle, nextAngle);
ctx.stroke();
const center = (angle + nextAngle) / 2;
ctx.fillText(`${i + 1}`, 0.5 + Math.cos(center) * 0.4, 0.5 + Math.sin(center) * 0.4);
angle = nextAngle;
}
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
ctx.lineWidth = 0.01;
angle = this.startAngle;
for (let i = 0; i < this.probabilities.length; i++) {
const nextAngle = angle + this.probabilities[i] * 2 * Math.PI;
ctx.beginPath();
ctx.moveTo(0.5, 0.5);
ctx.lineTo(0.5 + Math.cos(angle) * 0.5, 0.5 + Math.sin(angle) * 0.5)
ctx.stroke();
angle = nextAngle;
}
ctx.lineWidth = 0.02;
ctx.beginPath();
ctx.arc(0.5, 0.5, 0.5, 0, Math.PI * 2);
ctx.moveTo(0.5, 0.5);
ctx.lineTo(0.5 + Math.cos(this.angle) * 0.4, 0.5 + Math.sin(this.angle) * 0.4);
ctx.stroke();
}
public update(dt: number) {
if (this.fired) return;
if (this.speed < 0.1) {
this.fire();
return;
}
this.angle += this.speed * dt;
this.speed *= 1.0 - this.friction * dt;
this.friction += 0.7 * dt;
}
public spin() {
if (!this.fired) return;
this.fired = false;
this.speed = 25 + Math.random() * 25;
this.friction = 0.3 + Math.random() * 0.3;
}
public addListener(listener: SpinnerListener) {
this.listeners.add(listener);
}
public removeListener(listener: SpinnerListener) {
this.listeners.delete(listener);
}
private fire() {
if (this.fired) return;
this.fired = true;
let checkAngle = this.angle % (Math.PI * 2);
for (let a = 0; a < 2; a++) {
let angle = (this.startAngle + Math.PI * 2) % (Math.PI * 2);
for (let i = 0; i < this.probabilities.length; i++) {
const nextAngle = angle + this.probabilities[i] * 2 * Math.PI;
if (angle <= checkAngle && checkAngle <= nextAngle) {
this.listeners.forEach(l => l(i + 1));
return;
}
angle = nextAngle;
}
checkAngle += Math.PI * 2;
}
}
}