Files
Parchment/cave/index.js
2025-12-29 18:35:37 -06:00

184 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Samuel Russell
Captured Sun
12.29.2025
*/
class Canvas {
STROKE_COLOR = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim();
c = document.createElement("canvas")
ctx;
/* -----------------------------
Camera
----------------------------- */
camera = {
x: 0,
y: 0,
scale: 1,
ZOOM_SPEED: 0.016,
PAN_SPEED: 1.5,
FOCUS_THRESHOLD: 4.0
}
/* -----------------------------
Rectangle
----------------------------- */
rects = [];
resize = () => {
// Make Canvas Fill Screen
this.c.style.width = window.innerWidth + "px";
this.c.style.height = window.innerHeight + "px";
// Set Internal Render Size by DPR
const dpr = window.devicePixelRatio || 1;
this.c.width = window.innerWidth * dpr;
this.c.height = window.innerHeight * dpr;
this.ctx.scale(dpr, dpr);
}
onWheel = (e) => {
e.preventDefault();
let camera = this.camera
const rectCanvas = this.c.getBoundingClientRect();
const mouseX = e.clientX - rectCanvas.left;
const mouseY = e.clientY - rectCanvas.top;
if (!e.ctrlKey) {
const dpr = window.devicePixelRatio || 1;
// Two-finger pan in world coordinates
camera.x += (e.deltaX * dpr) * camera.PAN_SPEED / camera.scale;
camera.y += (e.deltaY * dpr) * camera.PAN_SPEED / camera.scale;
return;
}
const dpr = window.devicePixelRatio || 1;
const worldX = (
mouseX -
(this.c.width / dpr) / 2 // X coordinate of canvas center in CSS pixels
) // Mouse offset from canvas center in CSS pixels
/ camera.scale // Account for Zoom: shift by camera's zoom position
+ camera.x; // Account for Pan: shift by cameras world X position
const worldY = (
mouseY -
(this.c.height / dpr) / 2
)
/ camera.scale
+ camera.y;
// Apply zoom
const zoomFactor = Math.exp(-e.deltaY * camera.ZOOM_SPEED);
camera.scale *= zoomFactor;
camera.scale = Math.max(0.04, Math.min(10, camera.scale));
// Adjust camera to keep cursor fixed
camera.x = worldX - (mouseX - (this.c.width / dpr) / 2) / camera.scale;
camera.y = worldY - (mouseY - (this.c.height / dpr) / 2) / camera.scale;
}
draw = () => {
let ctx = this.ctx
let rect = this.rect
let camera = this.camera
let scale = camera.scale
requestAnimationFrame(this.draw);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, this.c.width, this.c.height);
let drawMenus = () => {
ctx.fillStyle = "#FEB279";
ctx.strokeRect(20,20,220,100);
ctx.fillStyle = "#000";
ctx.font = "60px arial";
// ctx.fillText("Button 1", 20, 100);
}
let drawSpaces = () => {
ctx.translate(this.c.width / 2, this.c.height / 2);
ctx.scale(scale, scale);
ctx.translate(-camera.x, -camera.y);
ctx.strokeStyle = this.STROKE_COLOR;
ctx.lineWidth = 0.5 / scale;
const baseRadius = 40; // distance of first ring
const ringSpacing = 70; // distance between rings
const dotRadius = 18; // circle size
let index = 0;
let ring = 0;
while (index < this.rects.length) {
// how many items fit on this ring
const circumference = 2 * Math.PI * (baseRadius + ring * ringSpacing);
const itemsInRing = Math.max(6, Math.floor(circumference / (dotRadius * 2.5)));
for (let i = 0; i < itemsInRing && index < this.rects.length; i++) {
const angle = (i / itemsInRing) * Math.PI * 2;
const r = baseRadius + ring * ringSpacing;
const x = Math.cos(angle) * r;
const y = Math.sin(angle) * r;
ctx.beginPath();
ctx.arc(x, y, dotRadius, 0, Math.PI * 2);
ctx.stroke();
const cssDiameter = dotRadius * 2 * scale;
if (cssDiameter >= 30) {
ctx.fillStyle = "#000";
ctx.font = `${3}px Arial`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(this.rects[index].name, x, y);
}
index++;
}
ring++;
}
};
ctx.save()
drawSpaces()
ctx.restore()
drawMenus(ctx);
}
async fetchDownloads() {
let res = await fetch("/downloads", {method: "GET"})
console.log(res)
let json = await res.json()
console.log(json)
this.rects = json
}
constructor() {
document.body.appendChild(this.c)
this.ctx = this.c.getContext("2d");
window.addEventListener("resize", this.resize);
this.resize();
this.c.addEventListener("wheel", this.onWheel, { passive: false });
this.draw()
this.fetchDownloads()
}
}
new Canvas()