As you wish
As a friend of mine pointed out, a fractal is more fun if you can zoom in on it! Rather than handle dragging & zooming myself, I’ll reach for Konva. A little longer this time, even with the help of Konva’s infinite canvas (edit: Apologies, I can’t seem to get pinch & zoom on mobile working, so this is only with the mouse wheel {for now?}):
const STAGE = new Konva.Stage({
container: "chaos",
width: window.innerWidth,
height: window.innerHeight,
draggable: true,
});
Konva.hitOnDragEnabled = true;
const LAYER = new Konva.Layer();
STAGE.add(LAYER);
const [XS, YS] = ((b) => {
const s = 0.75 * window.innerWidth;
const h = (s * Math.sqrt(3)) / 2;
return [
[b + 0, b + s / 2, b + s],
[b + h, b + 0, b + h],
];
})(10);
let [X, Y, ON] = [0, 0, null];
const new_xy = () => {
const [u, v] = [Math.sqrt(Math.random()), Math.random()];
X = (1 - u) * XS[0] + u * (1 - v) * XS[1] + u * v * XS[2];
Y = (1 - u) * YS[0] + u * (1 - v) * YS[1] + u * v * YS[2];
};
const draw = (x, y, radius = 2) => {
LAYER.add(
new Konva.Circle({
x,
y,
radius,
fill: "#027f8d",
}),
);
};
XS.forEach((x, i) => draw(x, YS[i], 4));
document.getElementById("reset").addEventListener("click", () => {
ON = clearInterval(ON);
LAYER.destroyChildren();
STAGE.scaleX(1);
STAGE.scaleY(1);
STAGE.position({ x: 0, y: 0 });
XS.forEach((x, i) => draw(x, YS[i], 4));
});
document.getElementById("start_stop").addEventListener("click", () => {
if (!ON) {
new_xy();
ON = setInterval(() => {
const i = Math.floor(XS.length * Math.random());
X += 0.5 * (XS[i] - X);
Y += 0.5 * (YS[i] - Y);
draw(X, Y);
});
} else {
ON = clearInterval(ON);
}
});
STAGE.on("wheel", (e) => {
e.evt.preventDefault();
const dir = (e.evt.deltaY > 0 ? -1 : 1) * (e.evt.ctrlKey ? -1 : 1);
const old = STAGE.scaleX();
const scale = Math.max(0.1, Math.min(10, dir > 0 ? old * 1.05 : old / 1.05));
STAGE.scale({ x: scale, y: scale });
const p = STAGE.getPointerPosition();
STAGE.position({
x: p.x - scale * ((p.x - STAGE.x()) / old),
y: p.y - scale * ((p.y - STAGE.y()) / old),
});
});