feat: implement interactive Periodic Table (PSE) modal with Bohr atom models and Steckbrief details

This commit is contained in:
kreidler90 2026-07-04 12:34:17 +00:00
parent e193638b70
commit d2253bfac3
3 changed files with 638 additions and 22 deletions

216
app.js
View File

@ -6,14 +6,23 @@ const discoveryProgress = document.getElementById('discovery-progress');
const atomCountEl = document.getElementById('atom-count'); const atomCountEl = document.getElementById('atom-count');
const moleculeCountEl = document.getElementById('molecule-count'); const moleculeCountEl = document.getElementById('molecule-count');
// Modal Elements // Main Modal Elements
const infoModal = document.getElementById('info-modal'); const infoModal = document.getElementById('info-modal');
const modalTitle = document.getElementById('modal-title'); const modalTitle = document.getElementById('modal-title');
const modalFormula = document.getElementById('modal-formula-badge'); const modalFormula = document.getElementById('modal-formula-badge');
const modalName = document.getElementById('modal-molecule-name'); const modalName = document.getElementById('modal-molecule-name');
const modalDesc = document.getElementById('modal-description'); const modalDesc = document.getElementById('modal-description');
const modalCloseBtn = document.getElementById('modal-close-btn'); const modalCloseBtn = document.getElementById('modal-close-btn');
const modalOverlay = document.querySelector('.modal-overlay'); const modalOverlay = document.querySelector('#info-modal .modal-overlay');
const modalCloseIcon = document.querySelector('#info-modal .modal-close-icon');
// PSE Modal Elements
const pseToggleBtn = document.getElementById('pse-toggle-btn');
const pseModal = document.getElementById('pse-modal');
const pseCloseBtn = document.getElementById('pse-close-btn');
const pseCloseIcon = document.getElementById('pse-close-icon');
const pseOverlay = document.querySelector('#pse-modal .modal-overlay');
const pseSpawnBtn = document.getElementById('pse-spawn-btn');
let elementsOnDesk = []; let elementsOnDesk = [];
let draggingElement = null; let draggingElement = null;
@ -23,6 +32,7 @@ let offsetY = 0;
// Elemente Datenbank für Spawnen (Symbole, Farben, Namen) // Elemente Datenbank für Spawnen (Symbole, Farben, Namen)
const elementDetails = { const elementDetails = {
'H': { color: '#ff7675', name: 'Wasserstoff' }, 'H': { color: '#ff7675', name: 'Wasserstoff' },
'He': { color: '#e84393', name: 'Helium' },
'O': { color: '#74b9ff', name: 'Sauerstoff' }, 'O': { color: '#74b9ff', name: 'Sauerstoff' },
'C': { color: '#2d3436', name: 'Kohlenstoff' }, 'C': { color: '#2d3436', name: 'Kohlenstoff' },
'N': { color: '#a29bfe', name: 'Stickstoff' }, 'N': { color: '#a29bfe', name: 'Stickstoff' },
@ -32,6 +42,31 @@ const elementDetails = {
'Fe': { color: '#b2bec3', name: 'Eisen' } 'Fe': { color: '#b2bec3', name: 'Eisen' }
}; };
// Periodensystem Elemente Datenbank (Wissenschaftliche Details & Schalenmodell)
const pseElements = [
{ number: 1, symbol: 'H', name: 'Wasserstoff', mass: '1.008 u', category: 'nichtmetall', row: 1, col: 1, playable: true, electrons: [1], desc: 'Das häufigste und leichteste Element im Universum. Es bildet den primären Kernbrennstoff für alle leuchtenden Sterne.' },
{ number: 2, symbol: 'He', name: 'Helium', mass: '4.003 u', category: 'edelgas', row: 1, col: 18, playable: true, electrons: [2], desc: 'Ein absolut reaktionsträges Edelgas. Es brennt nicht und ist viel leichter als Luft perfekt für Ballons und lässt Stimmen lustig hoch klingen!' },
{ number: 3, symbol: 'Li', name: 'Lithium', mass: '6.94 u', category: 'alkalimetall', row: 2, col: 1, playable: false, electrons: [2, 1], desc: 'Ein extrem leichtes, reaktives Metall. Es ist das Herzstück moderner Lithium-Ionen-Akkus für Handys, Laptops und Elektroautos.' },
{ number: 4, symbol: 'Be', name: 'Beryllium', mass: '9.012 u', category: 'erdalkali', row: 2, col: 2, playable: false, electrons: [2, 2], desc: 'Ein seltenes, hochgiftiges Erdalkalimetall. Es ist extrem steif und temperaturbeständig und wird deshalb im Weltraumteleskop-Bau (z.B. James Webb) eingesetzt.' },
{ number: 5, symbol: 'B', name: 'Bor', mass: '10.81 u', category: 'metalloid', row: 2, col: 13, playable: false, electrons: [2, 3], desc: 'Ein hartes, hitzebeständiges Halbmetall. Es wird für extrem widerstandsfähiges Glas (Borosilikatglas) und im Raketentreibstoff-Sektor verwendet.' },
{ number: 6, symbol: 'C', name: 'Kohlenstoff', mass: '12.011 u', category: 'nichtmetall', row: 2, col: 14, playable: true, electrons: [2, 4], desc: 'Die chemische Basis allen Lebens auf der Erde! Kohlenstoff kommt in reiner Form sowohl als weicher Graphit (Bleistift) als auch als extrem harter Diamant vor.' },
{ number: 7, symbol: 'N', name: 'Stickstoff', mass: '14.007 u', category: 'nichtmetall', row: 2, col: 15, playable: true, electrons: [2, 5], desc: 'Stickstoff bildet fast 78% unserer Atemluft! Er reagiert unter normalen Bedingungen kaum und schützt die Erde vor unkontrollierbaren Bränden.' },
{ number: 8, symbol: 'O', name: 'Sauerstoff', mass: '15.999 u', category: 'nichtmetall', row: 2, col: 16, playable: true, electrons: [2, 6], desc: 'Lebensnotwendig für fast alle Tiere. Es ist hochreaktiv und verbindet sich leidenschaftlich gern mit anderen Elementen (Rosten von Eisen, Verbrennung).' },
{ number: 9, symbol: 'F', name: 'Fluor', mass: '18.998 u', category: 'halogen', row: 2, col: 17, playable: false, electrons: [2, 7], desc: 'Das reaktivste aller chemischen Elemente! Fluor-Gas zerfrisst fast jeden Stoff sofort unter Flammenbildung. Spuren von Fluorid schützen unseren Zahnschmelz.' },
{ number: 10, symbol: 'Ne', name: 'Neon', mass: '20.180 u', category: 'edelgas', row: 2, col: 18, playable: false, electrons: [2, 8], desc: 'Ein extrem reaktionsträges Edelgas. Unter Hochspannung leuchtet es intensiv rötlich-orange, weshalb es der Namensgeber für alle Leuchtreklamen ist.' },
{ number: 11, symbol: 'Na', name: 'Natrium', mass: '22.990 u', category: 'alkalimetall', row: 3, col: 1, playable: true, electrons: [2, 8, 1], desc: 'Ein extrem reaktionsfreudiges, weiches Alkalimetall. Es reagiert explosionsartig mit Wasser und muss daher in zähem Öl (Petroleum) gelagert werden.' },
{ number: 12, symbol: 'Mg', name: 'Magnesium', mass: '24.305 u', category: 'erdalkali', row: 3, col: 2, playable: false, electrons: [2, 8, 2], desc: 'Ein leichtes Erdalkalimetall. Es brennt mit einer extrem hellen, blendend weißen Flamme und ist wichtig für die Muskel- und Nervenfunktion im Körper.' },
{ number: 13, symbol: 'Al', name: 'Aluminium', mass: '26.982 u', category: 'metall', row: 3, col: 13, playable: false, electrons: [2, 8, 3], desc: 'Das dritthäufigste Element der Erdkruste. Es ist leicht, rostfrei und wird für Flugzeuge, Alufolie, Getränkedosen und Autoteile verwendet.' },
{ number: 14, symbol: 'Si', name: 'Silicium', mass: '28.085 u', category: 'metalloid', row: 3, col: 14, playable: false, electrons: [2, 8, 4], desc: 'Das Element, das unsere Computertechnologie antreibt! Silicium ist ein Halbleiter und die absolute Basis aller Mikrochips und Solarzellen.' },
{ number: 15, symbol: 'P', name: 'Phosphor', mass: '30.974 u', category: 'nichtmetall', row: 3, col: 15, playable: false, electrons: [2, 8, 5], desc: 'Ein lebenswichtiges Element, das im menschlichen Erbgut (DNA) und in Knochen vorkommt. Roter Phosphor wird auf Streichholz-Reibflächen verwendet.' },
{ number: 16, symbol: 'S', name: 'Schwefel', mass: '32.06 u', category: 'nichtmetall', row: 3, col: 16, playable: true, electrons: [2, 8, 6], desc: 'Ein zitronengelber, geruchloser Feststoff. Seine organischen Verbindungen riechen oft extrem faulig (z.B. faule Eier). Schwefel ist wichtig für Haare und Proteine.' },
{ number: 17, symbol: 'Cl', name: 'Chlor', mass: '35.45 u', category: 'halogen', row: 3, col: 17, playable: true, electrons: [2, 8, 7], desc: 'Ein giftiges, stechend riechendes gelbgrünes Gas. Chlor ist extrem reaktiv und wird weltweit zur Desinfektion von Trinkwasser und Pools genutzt.' },
{ number: 18, symbol: 'Ar', name: 'Argon', mass: '39.948 u', category: 'edelgas', row: 3, col: 18, playable: false, electrons: [2, 8, 8], desc: 'Das dritthäufigste Gas in unserer Atmosphäre. Argon wird als ungiftiges Schutzgas beim Schweißen und in Doppelglasscheiben zur Wärmeisolierung eingesetzt.' },
{ number: 19, symbol: 'K', name: 'Kalium', mass: '39.098 u', category: 'alkalimetall', row: 4, col: 1, playable: false, electrons: [2, 8, 8, 1], desc: 'Ein weiches, hochreaktives Alkalimetall. Es reagiert noch heftiger mit Wasser als Natrium und ist essenziell für die Weiterleitung von Nervensignalen.' },
{ number: 20, symbol: 'Ca', name: 'Calcium', mass: '40.078 u', category: 'erdalkali', row: 4, col: 2, playable: false, electrons: [2, 8, 8, 2], desc: 'Der elementare Baustein für Skelette und Zähne! Calcium bildet Knochen, Eierschalen, Muscheln und Kreide und ist wichtig für die Muskelarbeit.' },
{ number: 26, symbol: 'Fe', name: 'Eisen', mass: '55.845 u', category: 'uebergang', row: 4, col: 8, playable: true, electrons: [2, 8, 14, 2], desc: 'Das am häufigsten genutzte Metall der Menschheit. Eisen bildet das Zentrum unseres Erdkerns (Magnetfeld) und transportiert Sauerstoff in unserem Blut (Hämoglobin).' }
];
// Rezepturenbuch (die Formeln müssen genau den Kombinationen der Symbole entsprechen) // Rezepturenbuch (die Formeln müssen genau den Kombinationen der Symbole entsprechen)
const recipes = [ const recipes = [
{ {
@ -158,18 +193,19 @@ const recipes = [
ingredients: ['C', 'C', 'H', 'H', 'H', 'H'], // 2x Kohlenstoff, 4x Wasserstoff ingredients: ['C', 'C', 'H', 'H', 'H', 'H'], // 2x Kohlenstoff, 4x Wasserstoff
result: 'C₂H₄ (Ethen)', result: 'C₂H₄ (Ethen)',
color: '#ffeaa7', color: '#ffeaa7',
desc: 'Ethen (Ethylen) ist ein gasförmiges Pflanzenhormon, das Früchte reifen lässt. Industriell ist es der absolut wichtigste Baustein zur Herstellung des Kunststoffs Polyethylen (PE).' desc: 'Ethen (Ethylen) is ein gasförmiges Pflanzenhormon, das Früchte reifen lässt. Industriell ist es der absolut wichtigste Baustein zur Herstellung des Kunststoffs Polyethylen (PE).'
} }
]; ];
// Geladene Entdeckungen aus dem LocalStorage // Geladene Entdeckungen aus dem LocalStorage
let discoveredMolecules = new Set(JSON.parse(localStorage.getItem('chem_lab_discoveries') || '[]')); let discoveredMolecules = new Set(JSON.parse(localStorage.getItem('chem_lab_discoveries') || '[]'));
let selectedPSEElement = null;
// Initiales Laden // Initiales Laden
updateStats(); updateStats();
renderDiscoveryBook(); renderDiscoveryBook();
// Button-Klicks registrieren für Spawnen // Button-Klicks registrieren für Spawnen in Sidebar
spawnBtns.forEach(btn => { spawnBtns.forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
const symbol = btn.dataset.element; const symbol = btn.dataset.element;
@ -185,14 +221,45 @@ clearDeskBtn.addEventListener('click', () => {
updateStats(); updateStats();
}); });
// Modal Events // =========================================
// MODALS LOGIC
// =========================================
// Info-Modal (Entdeckungen) Schließen
modalCloseBtn.addEventListener('click', closeModal); modalCloseBtn.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', closeModal); modalOverlay.addEventListener('click', closeModal);
modalCloseIcon.addEventListener('click', closeModal);
function closeModal() { function closeModal() {
infoModal.classList.add('hidden'); infoModal.classList.add('hidden');
} }
// PSE Modal Steuerung
pseToggleBtn.addEventListener('click', () => {
pseModal.classList.remove('hidden');
renderPSEGrid();
});
pseCloseBtn.addEventListener('click', closePSE);
pseCloseIcon.addEventListener('click', closePSE);
pseOverlay.addEventListener('click', closePSE);
function closePSE() {
pseModal.classList.add('hidden');
}
// PSE Atom Spawnen Event
pseSpawnBtn.addEventListener('click', () => {
if (selectedPSEElement) {
spawnAtom(selectedPSEElement.symbol, elementDetails[selectedPSEElement.symbol].color);
closePSE();
}
});
// =========================================
// GAMEPLAY CORE LOGIC
// =========================================
function spawnAtom(symbol, color) { function spawnAtom(symbol, color) {
const el = document.createElement('div'); const el = document.createElement('div');
el.classList.add('atom'); el.classList.add('atom');
@ -200,7 +267,7 @@ function spawnAtom(symbol, color) {
el.dataset.symbol = symbol; el.dataset.symbol = symbol;
el.style.backgroundColor = color; el.style.backgroundColor = color;
// Farblich angepasster Text für dunkle Elemente // Farblich angepasster Text für dunkle/kontrastreiche Elemente
if (symbol === 'C' || symbol === 'Fe') { if (symbol === 'C' || symbol === 'Fe') {
el.style.color = '#fff'; el.style.color = '#fff';
} else { } else {
@ -387,7 +454,7 @@ function triggerExplosion(x, y, color) {
const angle = Math.random() * Math.PI * 2; const angle = Math.random() * Math.PI * 2;
const velocity = 40 + Math.random() * 80; const velocity = 40 + Math.random() * 80;
const targetX = Math.cos(angle) * velocity; const targetX = Math.cos(angle) * velocity;
const targetY = Math.sin(angle) * velocity + 20; // Leichte Gravitation (nach unten gezogen) const targetY = Math.sin(angle) * velocity + 20; // Leichte Gravitation
p.animate([ p.animate([
{ transform: 'translate(0, 0) scale(1)', opacity: 1 }, { transform: 'translate(0, 0) scale(1)', opacity: 1 },
@ -405,10 +472,10 @@ function triggerExplosion(x, y, color) {
function showModal(recipe, isNew = true) { function showModal(recipe, isNew = true) {
if (isNew) { if (isNew) {
modalTitle.innerText = "Molekül entdeckt! 🎉"; modalTitle.innerText = "Molekül entdeckt! 🎉";
document.querySelector('.modal-emoji').innerText = "🎉"; document.querySelector('#info-modal .modal-emoji').innerText = "🎉";
} else { } else {
modalTitle.innerText = "Molekül-Lexikon 📖"; modalTitle.innerText = "Molekül-Lexikon 📖";
document.querySelector('.modal-emoji').innerText = "📖"; document.querySelector('#info-modal .modal-emoji').innerText = "📖";
} }
// Subscripts für Formel schön rendern // Subscripts für Formel schön rendern
@ -416,7 +483,7 @@ function showModal(recipe, isNew = true) {
modalFormula.innerHTML = prettyFormula; modalFormula.innerHTML = prettyFormula;
modalFormula.style.backgroundColor = recipe.color; modalFormula.style.backgroundColor = recipe.color;
// Reiner Name (Text in Klammern aus z.B. "H₂O (Wasser)") // Reiner Name (Text in Klammern)
const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')')); const cleanName = recipe.result.substring(recipe.result.indexOf('(') + 1, recipe.result.indexOf(')'));
modalName.innerText = cleanName; modalName.innerText = cleanName;
@ -472,3 +539,132 @@ function updateStats() {
atomCountEl.innerText = atoms; atomCountEl.innerText = atoms;
moleculeCountEl.innerText = molecules; moleculeCountEl.innerText = molecules;
} }
// =========================================
// INTERACTIVE PSE LOGIC
// =========================================
function renderPSEGrid() {
const grid = document.getElementById('pse-grid');
grid.innerHTML = '';
pseElements.forEach(el => {
const div = document.createElement('div');
div.classList.add('pse-el');
div.style.gridRow = el.row;
div.style.gridColumn = el.col;
div.dataset.symbol = el.symbol;
div.innerHTML = `
<span class="pse-el-number">${el.number}</span>
<span class="pse-el-symbol">${el.symbol}</span>
<span class="pse-el-name">${el.name}</span>
`;
if (el.playable) {
div.classList.add('active-el');
div.classList.add(el.category);
div.addEventListener('click', () => {
selectElementSteckbrief(el);
});
} else {
div.classList.add('locked-el');
div.addEventListener('click', () => {
// Selektiert dekorative Elemente trotzdem für den Steckbrief (Lerneffekt!), aber deaktiviert Spawnen
selectElementSteckbrief(el, false);
});
}
grid.appendChild(div);
});
}
function selectElementSteckbrief(el, canSpawn = true) {
selectedPSEElement = el;
const emptyDiv = document.querySelector('.steckbrief-empty');
const cardDiv = document.querySelector('.steckbrief-card');
emptyDiv.classList.add('hidden');
cardDiv.classList.remove('hidden');
const box = document.getElementById('steckbrief-box');
box.innerText = el.symbol;
box.className = ''; // Zurücksetzen
box.classList.add('active-el', el.category);
// Spezieller Textkontrast für dunkle Elemente im Steckbrief
if (el.symbol === 'C' || el.symbol === 'Fe') {
box.style.color = '#fff';
} else {
box.style.color = '#333';
}
document.getElementById('steckbrief-name').innerText = el.name;
// Kategoriennamen lesbar übersetzen
const categoryNames = {
'nichtmetall': 'Nichtmetall',
'edelgas': 'Edelgas',
'alkalimetall': 'Alkalimetall',
'erdalkali': 'Erdalkalimetall',
'uebergang': 'Übergangsmetall',
'halogen': 'Halogen',
'metalloid': 'Halbmetall',
'metall': 'Metall'
};
document.getElementById('steckbrief-category').innerText = categoryNames[el.category] || el.category;
document.getElementById('steckbrief-category').className = ''; // Reset
document.getElementById('steckbrief-category').classList.add(el.category);
document.getElementById('steckbrief-number').innerText = el.number;
document.getElementById('steckbrief-mass').innerText = el.mass;
document.getElementById('steckbrief-desc').innerText = el.desc;
// Spawnen-Button steuern
if (canSpawn) {
pseSpawnBtn.classList.remove('hidden');
} else {
pseSpawnBtn.classList.add('hidden');
}
// Bohr-Modell Schalen visualisieren
renderBohrModel(el.electrons);
}
function renderBohrModel(electronsArray) {
const shellsContainer = document.getElementById('bohr-shells');
shellsContainer.innerHTML = '';
electronsArray.forEach((numElectrons, shellIndex) => {
const shellDiv = document.createElement('div');
shellDiv.classList.add('bohr-shell');
// Dynamischer Schalendurchmesser basierend auf Schalenindex (Concentric Circles)
const diameter = 40 + (shellIndex * 30);
shellDiv.style.width = diameter + 'px';
shellDiv.style.height = diameter + 'px';
// Geschwindigkeit variieren für coole Dynamik
shellDiv.style.animationDuration = (6 + shellIndex * 4) + 's';
// Elektronenpunkte auf der Kreisbahn berechnen & platzieren
for (let i = 0; i < numElectrons; i++) {
const electron = document.createElement('div');
electron.classList.add('bohr-electron');
// Winkel für Gleichverteilung berechnen
const angle = (i / numElectrons) * Math.PI * 2;
const radius = diameter / 2;
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
// Auf der CSS-Kreisbahn zentrieren
electron.style.left = `calc(50% + ${x}px - 3px)`;
electron.style.top = `calc(50% + ${y}px - 3px)`;
shellDiv.appendChild(electron);
}
shellsContainer.appendChild(shellDiv);
});
}

View File

@ -17,9 +17,10 @@
<h3>Atome</h3> <h3>Atome</h3>
<div class="elements-grid"> <div class="elements-grid">
<div class="spawn-btn" data-element="H" data-color="#ff7675" title="Wasserstoff">H</div> <div class="spawn-btn" data-element="H" data-color="#ff7675" title="Wasserstoff">H</div>
<div class="spawn-btn" data-element="O" data-color="#74b9ff" title="Sauerstoff">O</div> <div class="spawn-btn" data-element="He" data-color="#e84393" title="Helium">He</div>
<div class="spawn-btn" data-element="C" data-color="#2d3436" title="Kohlenstoff">C</div> <div class="spawn-btn" data-element="C" data-color="#2d3436" title="Kohlenstoff">C</div>
<div class="spawn-btn" data-element="N" data-color="#a29bfe" title="Stickstoff">N</div> <div class="spawn-btn" data-element="N" data-color="#a29bfe" title="Stickstoff">N</div>
<div class="spawn-btn" data-element="O" data-color="#74b9ff" title="Sauerstoff">O</div>
<div class="spawn-btn" data-element="Na" data-color="#ffeaa7" title="Natrium">Na</div> <div class="spawn-btn" data-element="Na" data-color="#ffeaa7" title="Natrium">Na</div>
<div class="spawn-btn" data-element="Cl" data-color="#55efc4" title="Chlor">Cl</div> <div class="spawn-btn" data-element="Cl" data-color="#55efc4" title="Chlor">Cl</div>
<div class="spawn-btn" data-element="S" data-color="#fdcb6e" title="Schwefel">S</div> <div class="spawn-btn" data-element="S" data-color="#fdcb6e" title="Schwefel">S</div>
@ -48,7 +49,8 @@
<div id="stats"> <div id="stats">
Atome: <span id="atom-count">0</span> | Moleküle: <span id="molecule-count">0</span> Atome: <span id="atom-count">0</span> | Moleküle: <span id="molecule-count">0</span>
</div> </div>
<button id="clear-desk-btn">🧹 Tisch leeren</button> <button id="pse-toggle-btn" class="control-btn">📊 Periodensystem</button>
<button id="clear-desk-btn" class="control-btn danger">🧹 Tisch leeren</button>
</div> </div>
</div> </div>
<div id="workspace"></div> <div id="workspace"></div>
@ -61,6 +63,7 @@
<div class="modal-header"> <div class="modal-header">
<span class="modal-emoji">🎉</span> <span class="modal-emoji">🎉</span>
<h2 id="modal-title">Molekül entdeckt!</h2> <h2 id="modal-title">Molekül entdeckt!</h2>
<button class="modal-close-icon">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="modal-formula-badge">H₂O</div> <div id="modal-formula-badge">H₂O</div>
@ -73,6 +76,73 @@
</div> </div>
</div> </div>
<!-- INTERAKTIVES PERIODENSYSTEM (PSE) MODAL -->
<div id="pse-modal" class="modal hidden">
<div class="modal-overlay"></div>
<div class="modal-content pse-modal-content">
<div class="modal-header">
<span class="modal-emoji">📊</span>
<h2>Interaktives Periodensystem (PSE)</h2>
<button id="pse-close-icon" class="modal-close-icon">&times;</button>
</div>
<div class="pse-modal-body">
<!-- Linke Spalte: Das Gitter-System des Periodensystems -->
<div class="pse-grid-container">
<div id="pse-grid">
<!-- Dynamisch befüllt durch app.js -->
</div>
<div class="pse-legend">
<span class="legend-item"><span class="legend-color alkalimetall"></span>Alkalimetalle</span>
<span class="legend-item"><span class="legend-color erdalkali"></span>Erdalkali</span>
<span class="legend-item"><span class="legend-color uebergang"></span>Übergangsmetalle</span>
<span class="legend-item"><span class="legend-color metalloid"></span>Halbmetalle</span>
<span class="legend-item"><span class="legend-color nichtmetall"></span>Nichtmetalle</span>
<span class="legend-item"><span class="legend-color halogen"></span>Halogene</span>
<span class="legend-item"><span class="legend-color edelgas"></span>Edelgase</span>
</div>
</div>
<!-- Rechte Spalte: Der Element-Steckbrief -->
<div id="pse-steckbrief">
<div class="steckbrief-empty">
<p>Klicke auf ein leuchtendes Element im Periodensystem, um den wissenschaftlichen Steckbrief zu öffnen!</p>
</div>
<div class="steckbrief-card hidden">
<div class="steckbrief-header">
<div id="steckbrief-box">H</div>
<div>
<h3 id="steckbrief-name">Wasserstoff</h3>
<span id="steckbrief-category">Nichtmetall</span>
</div>
</div>
<div class="steckbrief-stats">
<div class="steckbrief-stat-box">
<span class="stat-label">Ordnungszahl</span>
<span id="steckbrief-number" class="stat-value">1</span>
</div>
<div class="steckbrief-stat-box">
<span class="stat-label">Atommasse</span>
<span id="steckbrief-mass" class="stat-value">1.008 u</span>
</div>
</div>
<!-- Bohr-Modell Visualisierung (CSS-Basiert) -->
<div class="bohr-model-container">
<div class="bohr-nucleus"></div>
<div id="bohr-shells">
<!-- Shells und Elektronen werden per JS gerendert -->
</div>
</div>
<p id="steckbrief-desc">Das häufigste und leichteste Element im Universum. Es bildet den Brennstoff von Sternen.</p>
<button id="pse-spawn-btn">⚛️ Atom auf Tisch spawnen</button>
</div>
</div>
</div>
<div class="modal-footer">
<button id="pse-close-btn">Schließen</button>
</div>
</div>
</div>
<script src="app.js"></script> <script src="app.js"></script>
</body> </body>
</html> </html>

370
style.css
View File

@ -8,6 +8,15 @@
--primary: #3498db; --primary: #3498db;
--primary-hover: #2980b9; --primary-hover: #2980b9;
--shadow: 0 4px 15px rgba(0, 0, 0, 0.3); --shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
/* PSE Category Colors */
--color-alkalimetall: #ff7675;
--color-erdalkali: #e17055;
--color-uebergang: #b2bec3;
--color-metalloid: #fdcb6e;
--color-nichtmetall: #74b9ff;
--color-halogen: #55efc4;
--color-edelgas: #e84393;
} }
* { * {
@ -89,7 +98,6 @@ body {
transition: transform 0.1s, box-shadow 0.1s; transition: transform 0.1s, box-shadow 0.1s;
} }
/* Element Colors defined in JS, but base defaults for fallback */
.spawn-btn:hover { .spawn-btn:hover {
transform: scale(1.1); transform: scale(1.1);
box-shadow: 0 6px 10px rgba(0,0,0,0.3); box-shadow: 0 6px 10px rgba(0,0,0,0.3);
@ -99,7 +107,6 @@ body {
transform: scale(0.95); transform: scale(0.95);
} }
/* Specific button text colors */
.spawn-btn[data-element="C"] { .spawn-btn[data-element="C"] {
color: #fff; color: #fff;
} }
@ -137,7 +144,6 @@ body {
padding-right: 5px; padding-right: 5px;
} }
/* Scrollbar styles */
#discovery-book::-webkit-scrollbar { #discovery-book::-webkit-scrollbar {
width: 6px; width: 6px;
} }
@ -217,7 +223,7 @@ body {
.desk-controls { .desk-controls {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 15px; gap: 12px;
} }
#stats { #stats {
@ -229,9 +235,7 @@ body {
border: 1px solid rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.05);
} }
#clear-desk-btn { .control-btn {
background-color: #e74c3c;
color: white;
border: none; border: none;
padding: 8px 15px; padding: 8px 15px;
border-radius: 6px; border-radius: 6px;
@ -239,13 +243,26 @@ body {
font-weight: bold; font-weight: bold;
font-size: 0.9rem; font-size: 0.9rem;
transition: background-color 0.2s, transform 0.1s; transition: background-color 0.2s, transform 0.1s;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
} }
#clear-desk-btn:hover { #pse-toggle-btn {
background-color: #0984e3;
color: white;
}
#pse-toggle-btn:hover {
background-color: #00cec9;
}
.control-btn.danger {
background-color: #e74c3c;
color: white;
}
.control-btn.danger:hover {
background-color: #c0392b; background-color: #c0392b;
} }
#clear-desk-btn:active { .control-btn:active {
transform: scale(0.95); transform: scale(0.95);
} }
@ -375,16 +392,31 @@ body {
padding: 20px; padding: 20px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 15px; justify-content: space-between;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
.modal-close-icon {
background: none;
border: none;
color: var(--text-muted);
font-size: 1.8rem;
cursor: pointer;
transition: color 0.2s;
}
.modal-close-icon:hover {
color: #e74c3c;
}
.modal-emoji { .modal-emoji {
font-size: 2rem; font-size: 2rem;
margin-right: 10px;
} }
.modal-header h2 { .modal-header h2 {
font-size: 1.3rem; font-size: 1.3rem;
flex-grow: 1;
} }
.modal-body { .modal-body {
@ -446,3 +478,321 @@ body {
#modal-close-btn:active { #modal-close-btn:active {
transform: scale(0.95); transform: scale(0.95);
} }
/* ========================================= */
/* INTERAKTIVES PERIODENSYSTEM (PSE) STYLES */
/* ========================================= */
.pse-modal-content {
width: 1100px;
max-width: 95%;
height: 650px;
display: flex;
flex-direction: column;
}
.pse-modal-body {
flex-grow: 1;
display: flex;
padding: 20px;
gap: 20px;
overflow: hidden;
}
/* Linke Spalte - PSE Gitter */
.pse-grid-container {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
#pse-grid {
display: grid;
grid-template-columns: repeat(18, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 6px;
padding: 10px;
background-color: rgba(0, 0, 0, 0.15);
border-radius: 12px;
border: 1px solid var(--border-color);
}
/* Einzelnes Element-Kästchen im PSE */
.pse-el {
aspect-ratio: 1;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.05);
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 4px;
font-size: 0.65rem;
user-select: none;
cursor: default;
transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s;
box-shadow: 0 2px 4px rgba(0,0,0,0.15);
}
.pse-el.active-el {
cursor: pointer;
border: 2px solid transparent;
}
.pse-el.active-el:hover {
transform: scale(1.15);
z-index: 10;
box-shadow: 0 5px 15px rgba(0,0,0,0.4);
}
.pse-el.locked-el {
opacity: 0.15;
background-color: rgba(255, 255, 255, 0.02);
}
.pse-el.locked-el:hover {
opacity: 0.3;
}
.pse-el-number {
font-size: 0.55rem;
color: var(--text-muted);
}
.pse-el-symbol {
font-size: 1.1rem;
font-weight: bold;
text-align: center;
line-height: 1;
}
.pse-el-name {
font-size: 0.5rem;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--text-muted);
}
.pse-el.active-el .pse-el-name,
.pse-el.active-el .pse-el-number {
color: #333;
}
.pse-el.active-el[data-symbol="C"] .pse-el-name,
.pse-el.active-el[data-symbol="C"] .pse-el-number {
color: var(--text-muted);
}
/* Category Color Highlights for active elements */
.alkalimetall { background-color: var(--color-alkalimetall); color: #333; border-color: rgba(0,0,0,0.15); }
.erdalkali { background-color: var(--color-erdalkali); color: #333; border-color: rgba(0,0,0,0.15); }
.uebergang { background-color: var(--color-uebergang); color: #333; border-color: rgba(0,0,0,0.15); }
.metalloid { background-color: var(--color-metalloid); color: #333; border-color: rgba(0,0,0,0.15); }
.nichtmetall { background-color: var(--color-nichtmetall); color: #333; border-color: rgba(0,0,0,0.15); }
.halogen { background-color: var(--color-halogen); color: #333; border-color: rgba(0,0,0,0.15); }
.edelgas { background-color: var(--color-edelgas); color: #333; border-color: rgba(0,0,0,0.15); }
.pse-el.active-el[data-symbol="C"] {
color: #fff;
}
/* Legend */
.pse-legend {
display: flex;
flex-wrap: wrap;
gap: 15px;
padding: 10px;
font-size: 0.75rem;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-muted);
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 3px;
display: inline-block;
}
/* Rechte Spalte: Steckbrief */
#pse-steckbrief {
width: 320px;
background-color: rgba(0, 0, 0, 0.2);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow-y: auto;
}
.steckbrief-empty {
text-align: center;
color: var(--text-muted);
font-size: 0.9rem;
line-height: 1.5;
}
.steckbrief-card {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 15px;
}
.steckbrief-header {
display: flex;
align-items: center;
gap: 15px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 12px;
}
#steckbrief-box {
width: 55px;
height: 55px;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
font-size: 1.6rem;
color: #333;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
#steckbrief-name {
font-size: 1.25rem;
color: #fff;
}
#steckbrief-category {
font-size: 0.75rem;
color: #ffeaa7;
background-color: rgba(255, 234, 167, 0.1);
padding: 2px 8px;
border-radius: 20px;
border: 1px solid rgba(255, 234, 167, 0.2);
}
.steckbrief-stats {
display: flex;
gap: 12px;
}
.steckbrief-stat-box {
flex: 1;
background-color: rgba(0, 0, 0, 0.15);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 8px;
text-align: center;
}
.stat-label {
font-size: 0.65rem;
color: var(--text-muted);
text-transform: uppercase;
display: block;
margin-bottom: 2px;
}
.stat-value {
font-size: 1rem;
font-weight: bold;
color: #fff;
}
#steckbrief-desc {
font-size: 0.85rem;
line-height: 1.4;
color: #dfe6e9;
flex-grow: 1;
}
#pse-spawn-btn {
background-color: #2ed573;
color: white;
border: none;
padding: 10px;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
font-size: 0.95rem;
transition: background-color 0.2s, transform 0.1s;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
width: 100%;
}
#pse-spawn-btn:hover {
background-color: #26af5a;
}
#pse-spawn-btn:active {
transform: scale(0.95);
}
/* Bohr Model (Atomschalen-Visualisierung) */
.bohr-model-container {
height: 140px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.25);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.03);
overflow: hidden;
}
.bohr-nucleus {
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #ff7675;
position: absolute;
box-shadow: 0 0 10px #ff7675;
z-index: 5;
}
.bohr-shell {
position: absolute;
border: 1px dashed rgba(255,255,255,0.15);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
animation: rotate-shell 20s linear infinite;
}
.bohr-shell:nth-child(1) { width: 50px; height: 50px; animation-duration: 8s; }
.bohr-shell:nth-child(2) { width: 90px; height: 90px; animation-duration: 15s; }
.bohr-shell:nth-child(3) { width: 130px; height: 130px; animation-duration: 25s; }
.bohr-electron {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #54a0ff;
position: absolute;
box-shadow: 0 0 5px #54a0ff;
}
@keyframes rotate-shell {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hidden {
display: none !important;
}