I wrote a program that animates the Recamán sequence. It is in uses HTML canvas and JavaScript. I was hoping for some feedback on how it could be improved.
"use strict";
function getMousePos(evt, can) {
var rect = can.getBoundingClientRect();
return {
x: evt.clientX - rect.x,
y: evt.clientY - rect.y,
}
}
function clearAllIntervals() {
var id = window.setTimeout(() => {}, 0);
while (id--) {
window.clearInterval(id);
}
}
class Arc {
constructor(x, y, r, ccw) {
this.x = x;
this.y = y;
this.r = r;
this.ccw = ccw;
}
draw() {
ctx.beginPath();
ctx.arc(this.x - offset.x, this.y - offset.y, this.r, 0, Math.PI, this.ccw);
ctx.stroke();
}
}
document.getElementById("stop").onclick = clearAllIntervals;
document.getElementById("zi").onclick = () => {
scale += 0.25;
ctx.scale(scale, scale);
}
document.getElementById("zo").onclick = () => {
scale -= 0.25;
ctx.scale(scale, scale);
}
document.getElementById("cls").onclick = () => {
arcs.splice(0, arcs.length);
ctx.clearRect(0, 0, canvas.width, canvas.height);
};
document.getElementById("hm").onclick = () => {
offset = { x: 0, y: 0 };
}
document.getElementById("anim").onclick = () => {
var speed = parseInt(document.getElementById("spd").value);
var size = parseInt(document.getElementById("size").value);
if (speed && size) {
draw(speed, size);
}
};
addEventListener("resize", () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
addEventListener("mousedown", (e) => {
pos = getMousePos(e, canvas);
});
addEventListener("mouseup", () => {
pos = undefined;
});
addEventListener("mousemove", (e) => {
if (pos !== undefined) {
var mouse = getMousePos(e, canvas);
offset.x += pos.x - mouse.x;
offset.y += pos.y - mouse.y;
pos = mouse;
}
});
const canvas = document.getElementById("disp");
const ctx = canvas.getContext("2d");
var arcs = [];
var offset = { x: 0, y: 0 };
var pos = undefined;
var scale = 1;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function draw(spd, siz) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
clearAllIntervals();
var xPos = 0;
var used = [];
arcs = [];
var i = siz;
var drawNext = () => {
if (i > 2500) {
return null;
}
let next = xPos - i;
if (used.includes(next) || next < 1) {
next = xPos + i;
}
let rad = (next - xPos) / 2;
arcs.push(new Arc(xPos + rad, canvas.height / 2, Math.abs(rad), i / siz % 2 || 0));
used.push(next);
xPos = next;
i += siz;
ctx.clearRect(0, 0, canvas.width, canvas.height);
arcs.forEach((arc) => {
arc.draw();
});
setTimeout(drawNext, 1000 / spd);
};
drawNext();
}
html, body {
height: 100%;
margin: 0;
overflow: hidden;
display: grid;
}
#disp {
height: 100vh;
width: 100vw;
margin: auto;
}
#menu {
position: absolute;
z-index: 1;
left: 10px;
top: 10px;
}
#menu input {
width: 50px;
}
button {
margin-bottom: 5px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Recáman Sequence</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="menu">
<span>Size: <input id="size" type="number" value="10"></span><br><br>
<span>Speed: <input id="spd" type="number" value="10"></span><br><br>
<span><button id="anim">Animate</button></span>
<span><button id="stop">Stop</button></span>
<span><button id="cls">Clear</button></span><br>
<span><button id="hm">Home</button></span>
<span><button id="zi" disabled="true">🔍+</button></span>
<span><button id="zo" disabled="true">🔍-</button></span>
</div>
<canvas id="disp"></canvas>
<script src="js/init.js"></script>
<script src="js/draw.js"></script>
</body>
</html>
This works best in full-screen.