Add advanced controls and synthesized rain sound to SubpageRain
This commit is contained in:
@@ -1,13 +1,74 @@
|
|||||||
---
|
---
|
||||||
// src/components/SubpageRain.astro
|
// src/components/SubpageRain.astro
|
||||||
---
|
---
|
||||||
<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="{ rainIntensity: 50, drops: [], playing: 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 }); } }" x-init="generateDrops(rainIntensity); $watch('rainIntensity', value => generateDrops(value))">
|
<!-- 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="{
|
||||||
|
rainIntensity: 50,
|
||||||
|
wind: 0,
|
||||||
|
lofiFilter: 800,
|
||||||
|
drops: [],
|
||||||
|
audioCtx: null,
|
||||||
|
noiseNode: null,
|
||||||
|
filterNode: null,
|
||||||
|
gainNode: null,
|
||||||
|
|
||||||
|
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)();
|
||||||
|
|
||||||
|
// Erzeuge weißes Rauschen
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Tiefpassfilter für den "Dumpf-Lofi"-Effekt
|
||||||
|
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();
|
||||||
|
} catch(e) {
|
||||||
|
console.log('Audio failed', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
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) -->
|
<!-- Rain Container (Full Screen on this page) -->
|
||||||
<div class="absolute inset-0 pointer-events-none z-0">
|
<div class="absolute inset-0 pointer-events-none z-0">
|
||||||
<template x-for="drop in drops">
|
<template x-for="drop in drops">
|
||||||
<div class="absolute bg-emerald-400/30 w-0.5 h-8"
|
<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`">
|
:style="`left: ${drop.left}%; top: -20px; animation: fall ${drop.speed}s linear infinite; animation-delay: ${drop.delay}s; transform: rotate(${wind}deg)`">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,26 +85,38 @@
|
|||||||
|
|
||||||
<!-- Controls -->
|
<!-- Controls -->
|
||||||
<div class="glass p-8 rounded-2xl border border-stone-800 space-y-6">
|
<div class="glass p-8 rounded-2xl border border-stone-800 space-y-6">
|
||||||
|
<!-- Intensity -->
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Regen-Intensität: <span x-text="rainIntensity" class="text-emerald-400"></span> Tropfen</label>
|
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Regen-Intensität: <span x-text="rainIntensity" class="text-emerald-400"></span> Tropfen</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">
|
<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>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between text-xs text-stone-600 font-mono mb-6">
|
<!-- Wind -->
|
||||||
<span>Nieselregen</span>
|
<div>
|
||||||
<span>Landregen</span>
|
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Windstärke (Neigung): <span x-text="wind" class="text-emerald-400"></span>°</label>
|
||||||
<span>Wolkenbruch</span>
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- Music Control -->
|
<!-- Lofi Filter -->
|
||||||
<div class="pt-4 border-t border-stone-800 space-y-4">
|
<div>
|
||||||
<div>
|
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Lofi-Filter (Muffigkeit): <span x-text="lofiFilter" class="text-emerald-400"></span> Hz</label>
|
||||||
<span class="block text-xs font-mono text-stone-500 uppercase mb-1 tracking-widest">Hintergrund-Vibe</span>
|
<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">
|
||||||
<span class="text-xs text-stone-400">Lofi Hip Hop Groove (YouTube Stream)</span>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="rounded-lg overflow-hidden border border-stone-800">
|
<div class="flex justify-between text-xs text-stone-600 font-mono pt-4 border-t border-stone-800">
|
||||||
<iframe width="100%" height="80" src="https://www.youtube.com/embed/jfKfPfyJRdk" title="Lofi Hip Hop" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
<span>Sound läuft automatisch</span>
|
||||||
</div>
|
<span class="text-emerald-400">Realtime Web Audio Rain</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- YouTube Player -->
|
||||||
|
<div class="glass p-6 rounded-2xl border border-stone-800 space-y-4">
|
||||||
|
<div>
|
||||||
|
<span class="block text-xs font-mono text-stone-500 uppercase mb-1 tracking-widest">Zusatz-Musik</span>
|
||||||
|
<span class="text-xs text-stone-400">Lofi Hip Hop Groove (YouTube Stream)</span>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg overflow-hidden border border-stone-800">
|
||||||
|
<iframe width="100%" height="80" src="https://www.youtube.com/embed/jfKfPfyJRdk" title="Lofi Hip Hop" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -58,9 +131,6 @@
|
|||||||
<h3 class="text-white font-bold text-lg">Vom Genuss zur Technik</h3>
|
<h3 class="text-white font-bold text-lg">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>
|
<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>
|
||||||
<p>Wir bauen Gärten, die das Wasser nicht einfach ableiten, sondern wie ein Schwamm aufsaugen und speichern. So schützen wir vor Überflutung und versorgen die Pflanzen in Trockenzeiten.</p>
|
<p>Wir bauen Gärten, die das Wasser nicht einfach ableiten, sondern wie ein Schwamm aufsaugen und speichern. So schützen wir vor Überflutung und versorgen die Pflanzen in Trockenzeiten.</p>
|
||||||
<button @click="currentPage = 'page-gewerbe'" class="text-emerald-400 hover:text-emerald-300 underline font-semibold transition-colors">
|
|
||||||
Mehr zum Schwammstadt-Prinzip lesen
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user