Skip to content

Commit a1be238

Browse files
authored
Merge pull request #9 from devsuoa/HH
Added artstyle to planets and asteroids
2 parents ad2b99d + 5ac5f78 commit a1be238

3 files changed

Lines changed: 211 additions & 2 deletions

File tree

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,6 @@ <h2 class="planet-panel__title" id="planetPanelTitle"></h2>
472472
<p class="zoom-hint" id="zoomHint">Solar system view — click the sun to zoom back in</p>
473473
</div>
474474

475-
<script type="module" src="/src/landing-page.ts"></script>
475+
<script type="module" src="/src/data/landing-page.ts"></script>
476476
</body>
477477
</html>

src/data/drawPlanets.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//rarh
2-
function drawPlanetCanvas(c2d, artKey, sz) {
2+
export function drawPlanetCanvas(c2d, artKey, sz) {
33
const r = sz;
44
c2d.clearRect(0, 0, r * 2, r * 2);
55
const cx2 = r, cy2 = r;

src/data/landing-page.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import { levels, type Level, type Planet } from "./planets.js";
2+
import { drawPlanetCanvas } from "./drawPlanets.js";
3+
4+
function hasOzoneLayer(p: Planet): boolean {
5+
const o = p as Planet & { ozoneLayer?: boolean; ozonLayer?: boolean };
6+
if (typeof o.ozoneLayer === "boolean") return o.ozoneLayer;
7+
if (typeof o.ozonLayer === "boolean") return o.ozonLayer;
8+
return false;
9+
}
10+
11+
function planetTooltip(p: Planet): string {
12+
const lines: string[] = [`${p.name} (${p.id})`, p.type];
13+
if (p.temperature != null) lines.push(`Temperature: ${p.temperature}°C`);
14+
if (p.waterCoverage != null) lines.push(`Water coverage: ${p.waterCoverage}%`);
15+
if (p.atmosphere) {
16+
lines.push(
17+
`Atmosphere: ${p.atmosphere}${p.atmosphereThickness ? ` (${p.atmosphereThickness})` : ""}`,
18+
);
19+
}
20+
if (p.mass != null) lines.push(`Mass: ${p.mass}× Earth`);
21+
if (p.weather) lines.push(`Weather: ${p.weather}`);
22+
lines.push(`Ozone layer: ${hasOzoneLayer(p) ? "Yes" : "No"}`);
23+
if (p.description) lines.push(p.description);
24+
return lines.join("\n");
25+
}
26+
27+
function appendStatRow(container: HTMLElement, label: string, value: string) {
28+
const row = document.createElement("div");
29+
row.className = "planet-panel__stat";
30+
const lab = document.createElement("span");
31+
lab.className = "planet-panel__label";
32+
lab.textContent = label;
33+
const val = document.createElement("span");
34+
val.className = "planet-panel__value";
35+
val.textContent = value;
36+
row.append(lab, val);
37+
container.append(row);
38+
}
39+
40+
function renderPlanetPanel(p: Planet): void {
41+
const title = document.getElementById("planetPanelTitle");
42+
const meta = document.getElementById("planetPanelMeta");
43+
const desc = document.getElementById("planetPanelDesc");
44+
const stats = document.getElementById("planetPanelStats");
45+
if (!title || !meta || !desc || !stats) return;
46+
47+
title.textContent = p.name;
48+
meta.textContent = `${p.id} · ${p.type}`;
49+
50+
desc.textContent = p.description ?? "";
51+
52+
stats.replaceChildren();
53+
if (p.temperature != null) appendStatRow(stats, "Temperature", `${p.temperature}°C`);
54+
if (p.waterCoverage != null) appendStatRow(stats, "Water coverage", `${p.waterCoverage}%`);
55+
if (p.atmosphere) {
56+
appendStatRow(
57+
stats,
58+
"Atmosphere",
59+
p.atmosphereThickness ? `${p.atmosphere} (${p.atmosphereThickness})` : p.atmosphere,
60+
);
61+
}
62+
if (p.mass != null) appendStatRow(stats, "Mass", `${p.mass}× Earth`);
63+
if (p.weather) appendStatRow(stats, "Weather", p.weather);
64+
if (p.type === "Meteor" && p.size) appendStatRow(stats, "Size", p.size);
65+
appendStatRow(stats, "Ozone layer", hasOzoneLayer(p) ? "Yes" : "No");
66+
}
67+
68+
const level1 = levels.find((l: Level) => l.id === 1);
69+
if (!level1) {
70+
throw new Error("planets.js: level 1 (System Solara) not found");
71+
}
72+
73+
const viewportEl = document.getElementById("viewport");
74+
const sunEl = document.getElementById("sun");
75+
const zoomHint = document.getElementById("zoomHint");
76+
const planetPanelEl = document.getElementById("planetPanel");
77+
const planetPanelClose = document.getElementById("planetPanelClose");
78+
79+
if (!viewportEl || !(sunEl instanceof HTMLButtonElement) || !planetPanelEl) {
80+
throw new Error("Landing DOM: #viewport, #sun, or #planetPanel missing");
81+
}
82+
83+
const viewport = viewportEl;
84+
const sun = sunEl;
85+
const planetPanel = planetPanelEl;
86+
87+
sun.title = `${level1.star.name}${level1.star.type}`;
88+
if (zoomHint) {
89+
zoomHint.textContent = `${level1.name} — click the sun to zoom back in`;
90+
}
91+
92+
const planetById = new Map(level1.planets.map((p: Planet) => [p.id, p]));
93+
let selectedPlanetId: string | null = null;
94+
95+
function setPanelOpen(open: boolean) {
96+
planetPanel.classList.toggle("planet-panel--open", open);
97+
planetPanel.setAttribute("aria-hidden", open ? "false" : "true");
98+
}
99+
100+
function clearPlanetSelection() {
101+
selectedPlanetId = null;
102+
document.querySelectorAll(".planet.planet--selected").forEach((el) => {
103+
el.classList.remove("planet--selected");
104+
});
105+
setPanelOpen(false);
106+
}
107+
108+
function selectPlanet(id: string) {
109+
const planet = planetById.get(id);
110+
if (!planet) return;
111+
112+
selectedPlanetId = id;
113+
document.querySelectorAll(".planet.planet--selected").forEach((el) => {
114+
el.classList.remove("planet--selected");
115+
});
116+
const dot = document.querySelector(`.planet[data-planet-id="${id}"]`);
117+
if (dot instanceof HTMLElement) dot.classList.add("planet--selected");
118+
119+
renderPlanetPanel(planet);
120+
setPanelOpen(true);
121+
planetPanelClose?.focus();
122+
}
123+
124+
const orbitEls = document.querySelectorAll(".system .orbit");
125+
level1.planets.forEach((planet: Planet, index: number) => {
126+
const orbit = orbitEls[index];
127+
if (!(orbit instanceof HTMLElement)) return;
128+
const dot = orbit.querySelector(".planet");
129+
if (!(dot instanceof HTMLElement)) return;
130+
131+
dot.className = "planet";
132+
dot.dataset.planetId = planet.id;
133+
dot.title = planetTooltip(planet);
134+
dot.setAttribute(
135+
"aria-label",
136+
`${planet.name}, ${planet.type}. ${planet.id}: orbit ${index + 1} from the star.`,
137+
);
138+
dot.setAttribute("role", "button");
139+
dot.tabIndex = -1;
140+
141+
// Determine size based on planet type
142+
const isMeteor = planet.type === "Meteor";
143+
const sz = isMeteor
144+
? planet.size === "Medium" ? 12 : 10
145+
: planet.type === "Ice Planet" ? 15 : 17;
146+
147+
// Create a canvas and draw the planet art onto it
148+
const canvas = document.createElement("canvas");
149+
canvas.width = sz * 2;
150+
canvas.height = sz * 2;
151+
canvas.style.width = `${sz * 2}px`;
152+
canvas.style.height = `${sz * 2}px`;
153+
canvas.style.borderRadius = "50%";
154+
canvas.style.display = "block";
155+
156+
dot.style.width = `${sz * 2}px`;
157+
dot.style.height = `${sz * 2}px`;
158+
dot.style.background = "none";
159+
dot.appendChild(canvas);
160+
161+
const c2d = canvas.getContext("2d");
162+
if (c2d) drawPlanetCanvas(c2d, (planet as any).artKey ?? "meteor", sz);
163+
164+
dot.addEventListener("click", (e) => {
165+
e.stopPropagation();
166+
if (!viewport.classList.contains("is-zoomed-out")) return;
167+
selectPlanet(planet.id);
168+
});
169+
170+
dot.addEventListener("keydown", (e) => {
171+
if (!viewport.classList.contains("is-zoomed-out")) return;
172+
if (e.key === "Enter" || e.key === " ") {
173+
e.preventDefault();
174+
selectPlanet(planet.id);
175+
}
176+
});
177+
});
178+
179+
function setZoomedOut(on: boolean) {
180+
viewport.classList.toggle("is-zoomed-out", on);
181+
sun.setAttribute(
182+
"aria-label",
183+
on ? "Zoom in: return to landing view" : "Zoom out: show full star system",
184+
);
185+
document.querySelectorAll(".planet").forEach((el) => {
186+
if (el instanceof HTMLElement) el.tabIndex = on ? 0 : -1;
187+
});
188+
if (!on) {
189+
clearPlanetSelection();
190+
}
191+
}
192+
193+
sun.addEventListener("click", () => {
194+
setZoomedOut(!viewport.classList.contains("is-zoomed-out"));
195+
});
196+
197+
planetPanelClose?.addEventListener("click", () => {
198+
clearPlanetSelection();
199+
});
200+
201+
viewport.addEventListener("click", (e) => {
202+
const t = e.target;
203+
if (!(t instanceof Node)) return;
204+
if (planetPanel.contains(t) || sun.contains(t)) return;
205+
if (t instanceof Element && t.closest(".planet")) return;
206+
if (viewport.classList.contains("is-zoomed-out") && selectedPlanetId) {
207+
clearPlanetSelection();
208+
}
209+
});

0 commit comments

Comments
 (0)