Herzlich willkommen zum SELF-Treffen 2026
vom 24.04. – 26.04.2026 in Halle (Saale)

Beispiel:Karaoke-4.html

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

<!DOCTYPE html> <html lang="de">

<head>

 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" media="screen" href="./Beispiel:Grundlayout.css">
 <title>Karaoke – Scarborough Fair</title>
 <style>
   :root {
     --zeilen-hoehe: 4.2rem;
     --sichtbare-zeilen: 4;
     --aktiv-farbe: #fffd59;
     --inaktiv-deckkraft: 0.35;
     --uebergangs-dauer: 0.35s;
     --box-hintergrund: #1a1a2e;
     --box-textfarbe: #e0e0e0;
     --schriftgroesse: 2rem;
   }
   body {
     font-family: sans-serif;
     max-width: 60em;
     margin: 1em auto;
     padding: 0 1rem 3rem;
   }
   audio {
     width: 100%;
   }
   .karaoke-box {
     position: relative;
     margin-top: 1.5rem;
     background: var(--box-hintergrund);
     border-radius: .75rem;
     height: calc(var(--zeilen-hoehe) * var(--sichtbare-zeilen));
     overflow: hidden;
   }
   .karaoke-box::before,
   .karaoke-box::after {
     content: "";
     position: absolute;
     left: 0;
     right: 0;
     height: var(--zeilen-hoehe);
     z-index: 1;
     pointer-events: none;
   }
   .karaoke-box::before {
     top: 0;
     background: linear-gradient(to bottom, var(--box-hintergrund), transparent);
   }
   .karaoke-box::after {
     bottom: 0;
     background: linear-gradient(to top, var(--box-hintergrund), transparent);
   }
   .karaoke-lauf {
     transition: transform var(--uebergangs-dauer) ease;
   }
   .karaoke-zeile {
     height: var(--zeilen-hoehe);
     display: flex;
     align-items: center;
     justify-content: center;
     font-size: var(--schriftgroesse);
     color: var(--box-textfarbe);
     opacity: var(--inaktiv-deckkraft);
     transition: opacity var(--uebergangs-dauer) ease, color var(--uebergangs-dauer) ease;
     padding: 0 1.5rem;
     text-align: center;
     margin: 0;
   }
   .karaoke-zeile.aktiv {
     opacity: 1;
     color: var(--aktiv-farbe);
     font-weight: bold;
   }
 </style>

</head>

<body>

🎤 Scarborough Fair

Karaoke-Player – Text scrollt mit, aktuelle Zeile ist markiert

 <audio id="abspieler" controls>
   <source src="/images/c/c8/Scarborough_Fair.mp3" type="audio/mpeg">
   Dein Browser unterstützt das Audio-Element nicht.
 </audio>
 <script>
   (function () {
     const lrcInhalt = String.raw`[ar:Traditional]

[ti:Scarborough Fair] [offset:0]

[00:00.50]Are you going to Scarborough Fair? [00:04.50]Parsley, sage, rose- mary and thyme, [00:09.50]Remember me to one who lives there, [00:15.50]She once was a true love of mine. [00:23.50]Tell her to make me a cambric shirt, [00:28.50]Parsley, sage, rose- mary and thyme, [00:33.50]Without no seams nor needle- work, [00:38.50]Then she'll be a true love of mine. [00:47.50]Tell her to find me an acre of land, [00:52.50]Parsley, sage, rose- mary and thyme, [00:57.50]Between the salt water and the sea strands, [01:02.50]Then she'll be a true love of mine. [01:11.00]Tell her to plough it with a sickle of leather, [01:16.00]Parsley, sage, rose- mary and thyme, [01:21.00]And bind it all with a bunch of heather, [01:26.00]Then she'll be a true love of mine. [01:35.00]Are you going to Scarborough Fair? [01:40.00]Parsley, sage, rose- mary and thyme, [01:45.00]Remember me to the one who lives there, [01:50.00]She once was a true love of mine.`;

     function lrcParsen(rohtext) {
       const zeitMuster = /\[(\d{2}):(\d{2}\.\d+)\](.+)/;
       return rohtext
         .split("\n")
         .map(zeile => {
           const treffer = zeile.match(zeitMuster);
           if (!treffer) return null;
           const minuten = parseFloat(treffer[1]);
           const sekunden = parseFloat(treffer[2]);
           return { zeit: minuten * 60 + sekunden, text: treffer[3].trim() };
         })
         .filter(Boolean)
         .sort((a, b) => a.zeit - b.zeit);
     }
     const zeilen = lrcParsen(lrcInhalt);
     const lauf = document.getElementById("lauf");
     const abstandOben = document.createElement("div");
     abstandOben.style.height = "var(--zeilen-hoehe)";
     lauf.appendChild(abstandOben);
     const zeilenElemente = zeilen.map(({ text }) => {
       const el = document.createElement("p");
       el.className = "karaoke-zeile";
       el.textContent = text;
       lauf.appendChild(el);
       return el;
     });
     const abstandUnten = document.createElement("div");
     abstandUnten.style.height = "var(--zeilen-hoehe)";
     lauf.appendChild(abstandUnten);
     const zeilenHoehe = zeilenElemente[0].getBoundingClientRect().height;
     const zeitstempel = zeilen.map(z => z.zeit);
     function aktivenIndexFinden(aktuelleZeit) {
       for (let i = zeitstempel.length - 1; i >= 0; i--) {
         if (aktuelleZeit >= zeitstempel[i]) return i;
       }
       return -1;
     }
     function zuIndexScrollen(index) {
       lauf.style.transform = `translateY(${-index * zeilenHoehe}px)`;
     }
     let aktiverIndex = -1;
     const audio = document.getElementById("abspieler");
     function karaokeAktualisieren() {
       const neuerIndex = aktivenIndexFinden(audio.currentTime);
       if (neuerIndex === aktiverIndex) return;
       if (aktiverIndex >= 0) zeilenElemente[aktiverIndex].classList.remove("aktiv");
       if (neuerIndex >= 0) {
         zeilenElemente[neuerIndex].classList.add("aktiv");
         zuIndexScrollen(neuerIndex);
       }
       aktiverIndex = neuerIndex;
     }
     audio.addEventListener("timeupdate", karaokeAktualisieren);
     audio.addEventListener("seeked", karaokeAktualisieren);
     audio.addEventListener("play", karaokeAktualisieren);
   })();
 </script>

</body> </html>