Files
brabetz-homepage/src/components/SubpageRain.astro
T

253 lines
14 KiB
Plaintext

---
// src/components/SubpageRain.astro
---
<script is:inline>
window.rainData = function() {
return {
rainIntensity: 50,
wind: 0,
lofiFilter: 800,
drops: [],
audioCtx: null,
noiseNode: null,
filterNode: null,
gainNode: null,
player: null,
rainOpen: false,
musicOpen: false,
generateDrops(val) {
this.drops = [];
for(let i=0; i<val; i++) this.drops.push({ left: Math.random()*100, speed: 0.5+Math.random()*0.5, delay: Math.random()*2 });
},
initAudio() {
if (this.audioCtx) return;
try {
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// --- REGEN-SOUND ---
let bufferSize = 2 * this.audioCtx.sampleRate;
let noiseBuffer = this.audioCtx.createBuffer(1, bufferSize, this.audioCtx.sampleRate);
let output = noiseBuffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1;
}
this.noiseNode = this.audioCtx.createBufferSource();
this.noiseNode.buffer = noiseBuffer;
this.noiseNode.loop = true;
this.filterNode = this.audioCtx.createBiquadFilter();
this.filterNode.type = 'lowpass';
this.filterNode.frequency.setValueAtTime(this.lofiFilter, this.audioCtx.currentTime);
this.gainNode = this.audioCtx.createGain();
this.gainNode.gain.setValueAtTime(this.rainIntensity / 200 * 0.05, this.audioCtx.currentTime);
this.noiseNode.connect(this.filterNode);
this.filterNode.connect(this.gainNode);
this.gainNode.connect(this.audioCtx.destination);
this.noiseNode.start();
// --- YOUTUBE API ---
this.loadYouTubeAPI();
} catch(e) {
console.log('Audio failed', e);
}
},
loadYouTubeAPI() {
if (window.YT && window.YT.Player) {
this.initPlayer();
} else {
let tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
let firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
window.onYouTubeIframeAPIReady = () => {
this.initPlayer();
};
}
},
initPlayer() {
this.player = new YT.Player('lofi-player', {
height: '100%',
width: '100%',
videoId: 'jfKfPfyJRdk',
playerVars: {
'autoplay': 1,
'mute': 0,
'controls': 1,
'rel': 0,
'modestbranding': 1,
'enablejsapi': 1
},
events: {
'onReady': (event) => {
let promise = event.target.playVideo();
if (promise && promise.catch) {
promise.catch(() => {
event.target.mute();
event.target.playVideo();
});
}
},
'onStateChange': (event) => {
if (event.data === YT.PlayerState.UNSTARTED || event.data === YT.PlayerState.PAUSED) {
setTimeout(() => {
if (this.player.getPlayerState() !== YT.PlayerState.PLAYING) {
this.player.mute();
this.player.playVideo();
}
}, 1000);
}
}
}
});
},
updateAudio() {
if (this.gainNode) {
this.gainNode.gain.linearRampToValueAtTime(this.rainIntensity / 200 * 0.05, this.audioCtx.currentTime + 0.2);
}
if (this.filterNode) {
this.filterNode.frequency.linearRampToValueAtTime(this.lofiFilter, this.audioCtx.currentTime + 0.2);
}
}
};
}
</script>
<!-- Subpage: Rain Detail -->
<div x-show="currentPage === 'page-rain'" x-cloak x-transition:enter="transition ease-out duration-500" x-transition:enter-start="opacity-0 translate-y-4" x-transition:enter-end="opacity-100 translate-y-0" class="min-h-screen pt-28 pb-24 px-6 lg:px-8 bg-stone-950 relative overflow-hidden"
x-data="window.rainData()"
x-init="generateDrops(rainIntensity); $watch('rainIntensity', value => { generateDrops(value); updateAudio() }); $watch('lofiFilter', value => updateAudio()); $watch('currentPage', val => { if(val === 'page-rain') { initAudio(); } })">
<!-- Rain Container (Full Screen on this page) -->
<div class="absolute inset-0 pointer-events-none z-0">
<template x-for="drop in drops">
<div class="absolute bg-emerald-400/30 w-0.5 h-8"
:style="`left: ${drop.left}%; top: -20px; animation: fall ${drop.speed}s linear infinite; animation-delay: ${drop.delay}s; transform: rotate(${wind}deg)`">
</div>
</template>
</div>
<div class="max-w-4xl mx-auto space-y-12 relative z-10">
<button @click="currentPage = 'main'" class="text-stone-400 hover:text-white transition-colors font-semibold text-xs uppercase tracking-wider flex items-center gap-2 bg-stone-900 px-5 py-2.5 rounded-lg border border-stone-800 hover:border-stone-700">
<i class="fa-solid fa-arrow-left"></i> Zurück zur Übersicht
</button>
<div class="border-b border-stone-800 pb-6">
<span class="text-xs text-emerald-500 font-mono uppercase tracking-widest block mb-2">Atmosphäre & Ökologie</span>
<h1 class="text-4xl sm:text-5xl font-luxury font-bold rainbow-text">Regen für Gemütlichkeit</h1>
</div>
<style>
.rainbow-text {
background: linear-gradient(to right, #ff2a2a, #ff7e2a, #ffea2a, #2aff2a, #2a7eff, #8a2be2, #ff2a2a);
background-size: 200% auto;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: rainbow 4s linear infinite;
}
@keyframes rainbow {
0% { background-position: 0% 50%; }
100% { background-position: 200% 50%; }
}
</style>
<!-- Controls (Togglebar) -->
<div class="glass p-8 rounded-2xl border border-stone-800">
<div class="flex justify-between items-center cursor-pointer select-none" @click="rainOpen = !rainOpen">
<div>
<span class="text-xs font-mono text-emerald-500 uppercase tracking-widest">Regen-Steuerung</span>
<span class="text-xs text-stone-500 block mt-1">Intensität, Wind und Filter</span>
</div>
<i class="fa-solid fa-chevron-down text-stone-500 transition-transform duration-300" :class="{ 'rotate-180': rainOpen }"></i>
</div>
<div x-show="rainOpen" x-cloak x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 -translate-y-2" x-transition:enter-end="opacity-100 translate-y-0" class="mt-6 pt-6 border-t border-stone-800">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Intensity -->
<div>
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Intensität: <span x-text="rainIntensity" class="text-emerald-400"></span></label>
<input type="range" min="0" max="200" x-model="rainIntensity" class="w-full h-2 bg-stone-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
</div>
<!-- Wind -->
<div>
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Wind: <span x-text="wind" class="text-emerald-400"></span>°</label>
<input type="range" min="-45" max="45" x-model="wind" class="w-full h-2 bg-stone-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
</div>
<!-- Lofi Filter -->
<div>
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Muffigkeit: <span x-text="lofiFilter" class="text-emerald-400"></span></label>
<input type="range" min="200" max="2000" x-model="lofiFilter" class="w-full h-2 bg-stone-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
</div>
</div>
<div class="flex justify-between text-xs text-stone-600 font-mono pt-4 mt-4 border-t border-stone-800">
<span>Sound läuft automatisch</span>
<span class="text-emerald-400">Realtime Web Audio Rain</span>
</div>
</div>
</div>
<!-- YouTube Player (Togglebar) -->
<div class="glass p-6 rounded-2xl border border-stone-800">
<div class="flex justify-between items-center cursor-pointer select-none" @click="musicOpen = !musicOpen">
<div>
<span class="block text-xs font-mono text-emerald-500 uppercase mb-1 tracking-widest">Musik (Lofi mit Vocals)</span>
<span class="text-xs text-stone-400">Echte Tracks für mehr Vielfalt & Gesang</span>
</div>
<i class="fa-solid fa-chevron-down text-stone-500 transition-transform duration-300" :class="{ 'rotate-180': musicOpen }"></i>
</div>
<div x-show="musicOpen" x-cloak x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 -translate-y-2" x-transition:enter-end="opacity-100 translate-y-0" class="space-y-4 mt-6 pt-6 border-t border-stone-800">
<div class="rounded-lg overflow-hidden border border-stone-800 aspect-video bg-stone-900">
<div id="lofi-player"></div>
</div>
<p class="text-xs text-stone-500 leading-relaxed">Browser blockieren Autoplay oft. Die KI versucht zuerst mit Ton abzuspielen. Falls das fehlschlägt, wird das Video stummgeschaltet automatisch gestartet. Du kannst den Ton dann einfach am Player aktivieren!</p>
</div>
</div>
<!-- Content -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 text-stone-400 text-sm leading-relaxed">
<div class="space-y-6">
<div>
<h3 class="text-white font-bold text-lg mb-2">Warum wir den Regen lieben</h3>
<p>Für viele ist Regen ein Grund, im Haus zu bleiben. Für uns Gärtner ist er der Herzschlag der Natur. Ohne Regen gibt es kein Wachstum, keine Kühlung und keine Erholung für den Boden.</p>
</div>
<div>
<h3 class="text-white font-bold text-base mb-1">Der Geruch des Lebens (Petrichor)</h3>
<p>Wusstest du, dass der Geruch von Regen wissenschaftlich erforscht ist? Eine <a href="https://news.mit.edu/2015/rainfall-releases-aerosols-0114" target="_blank" class="text-emerald-400 hover:underline">Studie des MIT</a> zeigt, dass Regentropfen beim Auftreffen auf poröse Böden winzige Luftblasen einschließen, die dann als Aerosole nach oben steigen und den Duft (Petrichor) verteilen. Dieser Duft signalisiert unserem Gehirn seit Jahrtausenden "Frische und Leben".</p>
</div>
<div>
<h3 class="text-white font-bold text-base mb-1">Beruhigung durch Rosa Rauschen</h3>
<p>Das gleichmäßige Prasseln von Regen ist ein natürliches "Rosa Rauschen". Im Gegensatz zu weißem Rauschen nehmen die Frequenzen bei höheren Tönen ab, was für das menschliche Ohr besonders angenehm ist. <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3412218/" target="_blank" class="text-emerald-400 hover:underline">Neurologische Studien</a> belegen, dass diese Klangmuster die Gehirnwellen stabilisieren und tiefen, erholsamen Schlaf fördern können.</p>
</div>
</div>
<div class="space-y-6">
<div>
<h3 class="text-white font-bold text-lg mb-2">Vom Genuss zur Technik</h3>
<p>Aber Regen ist nicht nur Atmosphäre. In Zeiten des Klimawandels müssen wir lernen, jeden Tropfen zu nutzen. Hier schließt sich der Kreis zu unserem **Schwammstadt-Prinzip**.</p>
</div>
<div>
<h3 class="text-white font-bold text-base mb-1">Gärten als Klimaretter</h3>
<p>Wir bauen Gärten, die das Wasser nicht einfach in die Kanalisation ableiten, sondern wie ein Schwamm aufsaugen und speichern. Das entlastet die städtische Infrastruktur bei Starkregen und kühlt die Umgebung in Hitzeperioden durch Verdunstung.</p>
</div>
<div>
<h3 class="text-white font-bold text-base mb-1">Die Psychologie der Natur</h3>
<p>Der Aufenthalt im Regen (oder das Betrachten und Hören davon) senkt nachweislich den Cortisolspiegel (Stresshormon). Wir möchten mit unseren Gärten Oasen schaffen, die dieses "Biophile Design" nutzen, um deine mentale Gesundheit zu fördern.</p>
</div>
</div>
</div>
</div>
</div>