diff --git a/assets/squids.png b/assets/squids.png new file mode 100644 index 0000000..bfa6b51 Binary files /dev/null and b/assets/squids.png differ diff --git a/index.html b/index.html index 4ff24b8..d41c745 100644 --- a/index.html +++ b/index.html @@ -1,37 +1,39 @@ - - SplatNet | Stages - - +
+ +
+ + SplatNet | Stages + + +
-
+
+ +
Loading
+
-
- -
Loading
-
- -
+
Stage Info
- Use the stage information to guide your weapon strategy and battle strategy! + Use the stage information to guide your weapon strategy and battle strategy!
-
+
- + - + diff --git a/stages.js b/stages.js index 648f5fc..0ce8e96 100644 --- a/stages.js +++ b/stages.js @@ -1,150 +1,241 @@ const API_URL = "https://api.splatcord.ink/prod/s1rotations"; function stageNames(stages, lang = "en-US") { - return stages.map(s => s.translatedNames[lang]); + return stages.map(s => s.translatedNames[lang]); } function stageImagePath(stageName) { - const firstWord = stageName - .split(" ")[0] - .toLowerCase() - .replace(/['.]/g, ""); - return `/assets/stages/${firstWord}.png`; + const firstWord = stageName + .split(" ")[0] + .toLowerCase() + .replace(/['.]/g, ""); + return `/assets/stages/${firstWord}.png`; } async function fetchRotations() { - try { - const res = await fetch(API_URL); - if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`); - const data = await res.json(); - const rotations = data?.pretendo?.rotations ?? {}; - const now = new Date(); - const timestamps = Object.keys(rotations).sort((a, b) => Number(a) - Number(b)); + try { + const res = await fetch(API_URL); + if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`); + const data = await res.json(); + const rotations = data?.pretendo?.rotations ?? {}; + const now = new Date(); + const timestamps = Object.keys(rotations).sort((a, b) => Number(a) - Number(b)); - const result = []; + const result = []; - for (const ts of timestamps) { - const start = new Date(Number(ts)); - const end = new Date(start.getTime() + 2 * 60 * 60 * 1000); - if (end < now) continue; + for (const ts of timestamps) { + const start = new Date(Number(ts)); + const end = new Date(start.getTime() + 2 * 60 * 60 * 1000); + if (end < now) continue; - const r = rotations[ts]; + const r = rotations[ts]; - result.push({ - turf: stageNames(r.turfStages), - ranked: stageNames(r.rankedStages), - ranked_mode: r.rankedMode.replace(/([a-z])([A-Z])/g, "$1 $2"), - startTime: start, - endTime: end - }); - } - - return result; - } catch (error) { - console.error("Failed to fetch rotations:", error); - return []; + result.push({ + turf: stageNames(r.turfStages), + ranked: stageNames(r.rankedStages), + ranked_mode: r.rankedMode.replace(/([a-z])([A-Z])/g, "$1 $2"), + startTime: start, + endTime: end, + }); } + + return result; + } catch (error) { + console.error("Failed to fetch rotations:", error); + return []; + } } function animateLoadingCanvas() { - const canvas = document.getElementById("loading-canvas"); - const loadingOverlay = document.getElementById("loading-overlay"); - if (!canvas || !loadingOverlay) return; - const ctx = canvas.getContext("2d"); - const image = new Image(); - image.src = "/assets/loading.png"; - const frameWidth = 100; - const frameHeight = 100; - const totalFrames = 17; - let currentFrame = 0; - let lastUpdateTime = 0; - let animationId; - image.onload = () => { - function animate(timestamp = 0) { - if (loadingOverlay.style.display === "none") { - cancelAnimationFrame(animationId); - return; - } - if (!lastUpdateTime) lastUpdateTime = timestamp; - const elapsed = timestamp - lastUpdateTime; - if (elapsed > 50) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage( - image, - 0, - currentFrame * frameHeight, - frameWidth, - frameHeight, - 0, - 0, - frameWidth, - frameHeight - ); - currentFrame = (currentFrame + 1) % totalFrames; - lastUpdateTime = timestamp; - } - animationId = requestAnimationFrame(animate); - } - animationId = requestAnimationFrame(animate); - }; + const canvas = document.getElementById("loading-canvas"); + const loadingOverlay = document.getElementById("loading-overlay"); + if (!canvas || !loadingOverlay) return; + const ctx = canvas.getContext("2d"); + const image = new Image(); + image.src = "/assets/loading.png"; + const frameWidth = 100; + const frameHeight = 100; + const totalFrames = 17; + let currentFrame = 0; + let lastUpdateTime = 0; + let animationId; + image.onload = () => { + function animate(timestamp = 0) { + if (loadingOverlay.style.display === "none") { + cancelAnimationFrame(animationId); + return; + } + if (!lastUpdateTime) lastUpdateTime = timestamp; + const elapsed = timestamp - lastUpdateTime; + if (elapsed > 50) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage( + image, + 0, + currentFrame * frameHeight, + frameWidth, + frameHeight, + 0, + 0, + frameWidth, + frameHeight + ); + currentFrame = (currentFrame + 1) % totalFrames; + lastUpdateTime = timestamp; + } + animationId = requestAnimationFrame(animate); + } + animationId = requestAnimationFrame(animate); + }; } +//pain in the ass to implement. im proud of ts +const canvas = document.getElementById("squid-canvas"); +const ctx = canvas.getContext("2d"); +const frameWidth = 64; +const frameHeight = 64; +const totalFrames = 8; +const squidCount = 5; +const squids = []; +const canvasWidth = canvas.width; +const canvasHeight = canvas.height; +const titleYThreshold = 0; + +function createSquid() { + return { + x: Math.random() * canvasWidth, + y: canvasHeight + Math.random() * 300, + currentFrame: Math.floor(Math.random() * totalFrames), + speed: 0.05 + Math.random() * 0.15, + lastFrameUpdate: 0 + }; +} + +for (let i = 0; i < squidCount; i++) { + squids.push(createSquid()); +} + +let lastTimestamp = 0; + +const image = new Image(); +image.src = "/assets/squids.png"; + +image.onload = () => { + function animate(timestamp = 0) { + if (!lastTimestamp) lastTimestamp = timestamp; + const delta = timestamp - lastTimestamp; + + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + + for (const squid of squids) { + squid.y -= squid.speed * delta; + + if (squid.y + frameHeight < titleYThreshold) { + squid.x = Math.random() * canvasWidth; + squid.y = canvasHeight + Math.random() * 300; + squid.currentFrame = Math.floor(Math.random() * totalFrames); + squid.speed = 0.05 + Math.random() * 0.15; + } + + if (timestamp - squid.lastFrameUpdate > 100) { + squid.currentFrame = (squid.currentFrame + 1) % totalFrames; + squid.lastFrameUpdate = timestamp; + } + + ctx.drawImage( + image, + squid.currentFrame * frameWidth, + 0, + frameWidth, + frameHeight, + squid.x, + squid.y, + frameWidth, + frameHeight + ); + } + + lastTimestamp = timestamp; + requestAnimationFrame(animate); + } + requestAnimationFrame(animate); +}; + async function renderStages() { - const loadingOverlay = document.getElementById("loading-overlay"); - const container = document.getElementById("stages-container"); - let rotations = []; - try { - rotations = await fetchRotations(); - } catch (err) { - console.error("Error in renderStages:", err); - } finally { - loadingOverlay.style.display = "none"; - } - if (!rotations.length) { - container.innerHTML = `

Something went wrong! Try reloading the page. If this problem persists you may be ratelimited.

`; - return; - } - for (const rotation of rotations) { - const optionsDate = { day: "2-digit", month: "2-digit" }; - const optionsTime = { hour: "numeric", minute: "2-digit", hour12: true }; - const timeZone = "Europe/Paris"; - const formattedTime = `${rotation.startTime.toLocaleDateString("en-GB", { ...optionsDate, timeZone })} at ${rotation.startTime.toLocaleTimeString("en-GB", { ...optionsTime, timeZone })} (CEST) ~ ${rotation.endTime.toLocaleDateString("en-GB", { ...optionsDate, timeZone })} at ${rotation.endTime.toLocaleTimeString("en-GB", { ...optionsTime, timeZone })} (CEST)`; + const loadingOverlay = document.getElementById("loading-overlay"); + const container = document.getElementById("stages-container"); + let rotations = []; + try { + rotations = await fetchRotations(); + } catch (err) { + console.error("Error in renderStages:", err); + } finally { + loadingOverlay.style.display = "none"; + } + if (!rotations.length) { + container.innerHTML = `

Something went wrong! Try reloading the page. If this problem persists you may be ratelimited.

`; + return; + } - container.innerHTML += ` -
${formattedTime}
+ let html = ""; -
- Regular Battle -
Regular Battle
-
- ${rotation.turf.map(name => ` -
- ${name} -
${name}
-
- `).join("")} -
-
+ for (const rotation of rotations) { + const optionsDate = { day: "2-digit", month: "2-digit" }; + const optionsTime = { hour: "numeric", minute: "2-digit", hour12: true }; + const timeZone = "Europe/Paris"; + const formattedTime = `${rotation.startTime.toLocaleDateString("en-GB", { + ...optionsDate, + timeZone, + })} at ${rotation.startTime.toLocaleTimeString("en-GB", { ...optionsTime, timeZone })} (CEST) ~ ${rotation.endTime.toLocaleDateString("en-GB", { + ...optionsDate, + timeZone, + })} at ${rotation.endTime.toLocaleTimeString("en-GB", { ...optionsTime, timeZone })} (CEST)`; -
- Ranked Battle -
Ranked Battle
-
-
Battle Mode
-
${rotation.ranked_mode}
-
-
- ${rotation.ranked.map(name => ` -
- ${name} -
${name}
-
- `).join("")} -
-
- `; - } + html += ` +
${formattedTime}
+ +
+ Regular Battle +
Regular Battle
+
+ ${rotation.turf + .map( + (name) => ` +
+ ${name} +
${name}
+
+ ` + ) + .join("")} +
+
+ +
+ Ranked Battle +
Ranked Battle
+
+
Battle Mode
+
${rotation.ranked_mode}
+
+
+ ${rotation.ranked + .map( + (name) => ` +
+ ${name} +
${name}
+
+ ` + ) + .join("")} +
+
+ `; + } + + container.innerHTML = html; } animateLoadingCanvas(); -renderStages(); +renderStages(); \ No newline at end of file diff --git a/styles.css b/styles.css index e99d8cb..20bede7 100644 --- a/styles.css +++ b/styles.css @@ -76,9 +76,6 @@ body { flex-direction: column; gap: 12px; width: 100%; -} - -.stages-section { background-color: rgba(0, 0, 0, 0.7); border-radius: 14px; padding: 16px 18px 20px; @@ -103,26 +100,25 @@ body { } .stage-title { - font-family: "regular"; + font-family: "Regular", sans-serif; text-align: center; margin-top: 6px; font-size: 28px; color: white; - margin-top: 6px; } .stages img { width: 100%; height: auto; display: block; - margin-bottom: 20px; - margin-top: 20px; + margin: 20px 0; } .ranked-mode-labels { text-align: center; margin-bottom: 12px; } + .battle-mode-text { font-family: "Splatoon1", sans-serif; color: white; @@ -130,6 +126,7 @@ body { letter-spacing: 1px; margin-bottom: 4px; } + .ranked-mode-text { font-family: "Regular", sans-serif; color: white; @@ -155,15 +152,14 @@ body { .mode-title { display: block; - margin: 4px auto 4px; + margin: 4px auto; max-width: 166px; width: 80%; height: auto; } .mode-text { - margin-top: 0px; - margin-bottom: 28px; + margin: 0 0 28px; text-align: center; color: white; font-family: "Splatoon1", sans-serif; @@ -172,7 +168,6 @@ body { user-select: none; } - .loading-overlay { position: fixed; top: 50%; @@ -194,6 +189,31 @@ body { box-sizing: border-box; } +#squid-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + overflow: visible; + pointer-events: none; + z-index: 0; +} + +#squid-canvas { + position: absolute; + left: 50%; + top: 0; + transform: translateX(-50%); + width: 1920px; + height: 1080px; + will-change: transform; + pointer-events: none; + user-select: none; + z-index: 0; + display: block; +} + #loading-canvas { image-rendering: pixelated; width: 100px;