Implement custom UI controls for YouTube Lofi player
This commit is contained in:
@@ -2,13 +2,44 @@
|
|||||||
// src/components/SubpageRain.astro
|
// src/components/SubpageRain.astro
|
||||||
---
|
---
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
|
// Load YouTube API
|
||||||
|
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.player = null;
|
||||||
|
window.onYouTubeIframeAPIReady = function() {
|
||||||
|
window.player = new YT.Player('lofi-player', {
|
||||||
|
height: '0',
|
||||||
|
width: '0',
|
||||||
|
videoId: 'jfKfPfyJRdk',
|
||||||
|
playerVars: {
|
||||||
|
'autoplay': 1,
|
||||||
|
'controls': 0,
|
||||||
|
'mute': 0
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
'onReady': function(event) {
|
||||||
|
console.log('YouTube Player Ready');
|
||||||
|
// Set initial volume
|
||||||
|
if(window.playerData) {
|
||||||
|
event.target.setVolume(window.playerData.lofiVolume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
window.rainData = function() {
|
window.rainData = function() {
|
||||||
return {
|
// Store reference to data for API callback
|
||||||
|
let data = {
|
||||||
rainIntensity: 50,
|
rainIntensity: 50,
|
||||||
wind: 0,
|
wind: 0,
|
||||||
lofiFilter: 800,
|
lofiFilter: 800,
|
||||||
musicVolume: 30,
|
musicVolume: 30,
|
||||||
lofiSrc: '',
|
lofiVolume: 50,
|
||||||
|
lofiPlaying: true,
|
||||||
drops: [],
|
drops: [],
|
||||||
audioCtx: null,
|
audioCtx: null,
|
||||||
noiseNode: null,
|
noiseNode: null,
|
||||||
@@ -26,7 +57,7 @@ window.rainData = function() {
|
|||||||
try {
|
try {
|
||||||
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
|
||||||
// --- REGEN-SOUND (Weißes Rauschen) ---
|
// --- REGEN-SOUND ---
|
||||||
let bufferSize = 2 * this.audioCtx.sampleRate;
|
let bufferSize = 2 * this.audioCtx.sampleRate;
|
||||||
let noiseBuffer = this.audioCtx.createBuffer(1, bufferSize, this.audioCtx.sampleRate);
|
let noiseBuffer = this.audioCtx.createBuffer(1, bufferSize, this.audioCtx.sampleRate);
|
||||||
let output = noiseBuffer.getChannelData(0);
|
let output = noiseBuffer.getChannelData(0);
|
||||||
@@ -51,14 +82,14 @@ window.rainData = function() {
|
|||||||
|
|
||||||
this.noiseNode.start();
|
this.noiseNode.start();
|
||||||
|
|
||||||
// --- MELODIE-SOUND (Echte "Easy Music") ---
|
// --- MELODIE-SOUND ---
|
||||||
let melodyOsc = this.audioCtx.createOscillator();
|
let melodyOsc = this.audioCtx.createOscillator();
|
||||||
this.melodyGain = this.audioCtx.createGain();
|
this.melodyGain = this.audioCtx.createGain();
|
||||||
melodyOsc.type = 'sine';
|
melodyOsc.type = 'sine';
|
||||||
|
|
||||||
let lowpass = this.audioCtx.createBiquadFilter();
|
let lowpass = this.audioCtx.createBiquadFilter();
|
||||||
lowpass.type = 'lowpass';
|
lowpass.type = 'lowpass';
|
||||||
lowpass.frequency.setValueAtTime(400, this.audioCtx.currentTime); // Sehr dumpf und weich
|
lowpass.frequency.setValueAtTime(400, this.audioCtx.currentTime);
|
||||||
|
|
||||||
melodyOsc.connect(lowpass);
|
melodyOsc.connect(lowpass);
|
||||||
lowpass.connect(this.melodyGain);
|
lowpass.connect(this.melodyGain);
|
||||||
@@ -66,8 +97,7 @@ window.rainData = function() {
|
|||||||
|
|
||||||
melodyOsc.start();
|
melodyOsc.start();
|
||||||
|
|
||||||
// Langsame, beruhigende Pentatonik-Folge
|
let notes = [261.63, 329.63, 392.00, 440.00, 392.00, 329.63];
|
||||||
let notes = [261.63, 329.63, 392.00, 440.00, 392.00, 329.63]; // C4, E4, G4, A4, G4, E4
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@@ -75,14 +105,13 @@ window.rainData = function() {
|
|||||||
let freq = notes[i];
|
let freq = notes[i];
|
||||||
melodyOsc.frequency.setValueAtTime(freq, now);
|
melodyOsc.frequency.setValueAtTime(freq, now);
|
||||||
|
|
||||||
// Sanftes Ein- und Ausblenden der Note
|
|
||||||
let maxVol = (this.musicVolume / 100) * 0.05;
|
let maxVol = (this.musicVolume / 100) * 0.05;
|
||||||
this.melodyGain.gain.setValueAtTime(0, now);
|
this.melodyGain.gain.setValueAtTime(0, now);
|
||||||
this.melodyGain.gain.linearRampToValueAtTime(maxVol, now + 1.5);
|
this.melodyGain.gain.linearRampToValueAtTime(maxVol, now + 1.5);
|
||||||
this.melodyGain.gain.linearRampToValueAtTime(0, now + 3.5);
|
this.melodyGain.gain.linearRampToValueAtTime(0, now + 3.5);
|
||||||
|
|
||||||
i = (i + 1) % notes.length;
|
i = (i + 1) % notes.length;
|
||||||
}, 4000); // Alle 4 Sekunden eine Note
|
}, 4000);
|
||||||
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log('Audio failed', e);
|
console.log('Audio failed', e);
|
||||||
@@ -96,15 +125,35 @@ window.rainData = function() {
|
|||||||
if (this.filterNode) {
|
if (this.filterNode) {
|
||||||
this.filterNode.frequency.linearRampToValueAtTime(this.lofiFilter, this.audioCtx.currentTime + 0.2);
|
this.filterNode.frequency.linearRampToValueAtTime(this.lofiFilter, this.audioCtx.currentTime + 0.2);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleLofi() {
|
||||||
|
if (window.player) {
|
||||||
|
if (this.lofiPlaying) {
|
||||||
|
window.player.pauseVideo();
|
||||||
|
this.lofiPlaying = false;
|
||||||
|
} else {
|
||||||
|
window.player.playVideo();
|
||||||
|
this.lofiPlaying = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateLofiVolume() {
|
||||||
|
if (window.player) {
|
||||||
|
window.player.setVolume(this.lofiVolume);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
window.playerData = data; // Make accessible to API
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Subpage: Rain Detail -->
|
<!-- 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"
|
<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-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(); lofiSrc = 'https://www.youtube.com/embed/jfKfPfyJRdk?autoplay=1'; } else { lofiSrc = ''; } })">
|
x-init="generateDrops(rainIntensity); $watch('rainIntensity', value => { generateDrops(value); updateAudio() }); $watch('lofiFilter', value => updateAudio()); $watch('lofiVolume', value => updateLofiVolume()); $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">
|
||||||
@@ -147,7 +196,7 @@ window.rainData = function() {
|
|||||||
|
|
||||||
<!-- Music Volume -->
|
<!-- Music Volume -->
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Musik-Lautstärke: <span x-text="musicVolume" class="text-emerald-400"></span>%</label>
|
<label class="block text-xs font-mono text-stone-500 uppercase mb-2 tracking-widest">Melodie-Lautstärke: <span x-text="musicVolume" class="text-emerald-400"></span>%</label>
|
||||||
<input type="range" min="0" max="100" x-model="musicVolume" class="w-full h-2 bg-stone-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
|
<input type="range" min="0" max="100" x-model="musicVolume" class="w-full h-2 bg-stone-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -157,15 +206,28 @@ window.rainData = function() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- YouTube Player -->
|
<!-- Custom Lofi Controls -->
|
||||||
<div class="glass p-6 rounded-2xl border border-stone-800 space-y-4">
|
<div class="glass p-6 rounded-2xl border border-stone-800 space-y-4">
|
||||||
<div>
|
<div class="flex justify-between items-center">
|
||||||
<span class="block text-xs font-mono text-stone-500 uppercase mb-1 tracking-widest">Zusatz-Musik</span>
|
<div>
|
||||||
<span class="text-xs text-stone-400">Lofi Hip Hop Groove (YouTube Stream)</span>
|
<span class="block text-xs font-mono text-stone-500 uppercase mb-1 tracking-widest">Zusatz-Musik</span>
|
||||||
</div>
|
<span class="text-xs text-stone-400">Lofi Hip Hop Groove (YouTube Stream)</span>
|
||||||
<div class="rounded-lg overflow-hidden border border-stone-800">
|
</div>
|
||||||
<iframe width="100%" height="80" :src="lofiSrc" title="Lofi Hip Hop" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
<div class="flex items-center gap-4">
|
||||||
|
<!-- Play/Pause -->
|
||||||
|
<button @click="toggleLofi()" class="text-white text-xs font-mono flex items-center gap-2 bg-stone-900 px-5 py-2.5 rounded-lg border border-stone-800 hover:border-emerald-500/50 transition-colors">
|
||||||
|
<i class="fa-solid" :class="lofiPlaying ? 'fa-pause text-emerald-400' : 'fa-play text-emerald-400'"></i>
|
||||||
|
<span x-text="lofiPlaying ? 'Pause' : 'Play'"></span>
|
||||||
|
</button>
|
||||||
|
<!-- Volume -->
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<i class="fa-solid fa-volume-low text-stone-500"></i>
|
||||||
|
<input type="range" min="0" max="100" x-model="lofiVolume" class="w-24 h-1 bg-stone-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Hidden YouTube Player -->
|
||||||
|
<div id="lofi-player"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
|
|||||||
Reference in New Issue
Block a user