Benutzer-Werkzeuge

Webseiten-Werkzeuge


tools:piano

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Nächste Überarbeitung
Vorherige Überarbeitung
tools:piano [29/03/2026 16:13] – angelegt Eric Webertools:piano [30/03/2026 15:32] (aktuell) Eric Weber
Zeile 1: Zeile 1:
 <html> <html>
-<!-- 
-  Interaktive Klaviatur mit Notenzeile 
-  Einbindung in DokuWiki: Inhalt zwischen den <div id="piano-app">...</div>-Tags einfügen 
-  Alle CSS-Stile sind auf #piano-app begrenzt und beeinflussen die restliche Seite nicht. 
---> 
- 
 <div id="piano-app"> <div id="piano-app">
  
Zeile 166: Zeile 160:
  
     /* --- Staff (Notenzeile) --- */     /* --- Staff (Notenzeile) --- */
 +    #piano-app .pa-staff-outer {
 +      margin-bottom: 14px;
 +    }
 +
 +    #piano-app .pa-staff-resize-bar {
 +      display: flex;
 +      align-items: center;
 +      justify-content: space-between;
 +      margin-bottom: 6px;
 +    }
 +
 +    #piano-app .pa-staff-label {
 +      font-size: 0.7rem;
 +      color: var(--pa-text-muted);
 +      font-family: var(--pa-font-body);
 +    }
 +
 +    #piano-app .pa-staff-size-wrap {
 +      display: flex;
 +      align-items: center;
 +      gap: 6px;
 +    }
 +
 +    #piano-app .pa-staff-size-wrap span {
 +      font-size: 0.7rem;
 +      color: var(--pa-text-muted);
 +    }
 +
 +    #piano-app .pa-staff-size-wrap input[type=range] {
 +      all: unset;
 +      width: 90px;
 +      height: 4px;
 +      background: var(--pa-border);
 +      border-radius: 2px;
 +      cursor: pointer;
 +      -webkit-appearance: none;
 +      appearance: none;
 +    }
 +
 +    #piano-app .pa-staff-size-wrap input[type=range]::-webkit-slider-thumb {
 +      -webkit-appearance: none;
 +      width: 14px;
 +      height: 14px;
 +      border-radius: 50%;
 +      background: var(--pa-gold);
 +      cursor: pointer;
 +    }
 +
     #piano-app .pa-staff-wrap {     #piano-app .pa-staff-wrap {
       background: #fff;       background: #fff;
       border-radius: 10px;       border-radius: 10px;
-      padding: 12px 24px; +      padding: 16px 20px;
-      margin-bottom: 18px;+
       overflow: hidden;       overflow: hidden;
       position: relative;       position: relative;
 +      display: flex;
 +      align-items: center;
 +      gap: 0;
 +      transition: padding 0.1s;
     }     }
  
-    #piano-app .pa-staff-label +    /* Schlüssel-Badge: runder Kreis mit dem Zeichen */ 
-      font-size: 0.7rem+    #piano-app .pa-clef-badge 
-      color: #888+      flex-shrink: 0; 
-      margin-bottom6px+      displayflex; 
-      font-family: var(--pa-font-body);+      align-items: center; 
 +      justify-content: center; 
 +      width: 52px; 
 +      height: 52px; 
 +      border-radius: 50%; 
 +      background: #f5f5f0; 
 +      border: 2px solid #ccc
 +      margin-right10px
 +      font-family: serif; 
 +      color: #222; 
 +      line-height: 1; 
 +      /* font-size wird per JS gesetzt */
     }     }
  
     #piano-app svg.pa-staff-svg {     #piano-app svg.pa-staff-svg {
-      display: block+  height: auto; /* Höhe dynamisch */ 
-      width: 100%; +  min-height: 60px; 
-      overflowvisible;+
 + 
 +    /* --- Oktaven-Scrollbar (sichtbar auf allen Geräten) --- */ 
 +    #piano-app .pa-octave-bar { 
 +      display: flex; 
 +      align-items: center; 
 +      gap: 10px; 
 +      padding: 6px 10px 2px; 
 +      background: var(--pa-surface); 
 +      border-bottom: 1px solid var(--pa-border); 
 +      border-radius: 0; 
 +    } 
 + 
 +    #piano-app .pa-octave-bar span { 
 +      font-size: 0.72rem; 
 +      color: var(--pa-text-muted); 
 +      white-space: nowrap; 
 +    } 
 + 
 +    #piano-app .pa-octave-bar input[type=range] { 
 +      all: unset; 
 +      flex: 1; 
 +      height: 6px; 
 +      background: var(--pa-border); 
 +      border-radius: 3px; 
 +      cursor: pointer; 
 +      -webkit-appearance: none; 
 +      appearance: none; 
 +    } 
 + 
 +    #piano-app .pa-octave-bar input[type=range]::-webkit-slider-thumb { 
 +      -webkit-appearance: none
 +      width: 22px; 
 +      height: 22px; 
 +      border-radius: 50%; 
 +      background: var(--pa-accent); 
 +      cursor: pointer; 
 +      border: 3px solid #fff; 
 +      box-shadow: 0 1px 4px rgba(0,0,0,0.3); 
 +    } 
 + 
 +    #piano-app .pa-octave-bar input[type=range]::-moz-range-thumb { 
 +      width: 22px; 
 +      height: 22px; 
 +      border-radius: 50%; 
 +      background: var(--pa-accent); 
 +      cursor: pointer; 
 +      border3px solid #fff;
     }     }
  
-    /* --- Piano Container (fest, kein Drag) --- */+    /* --- Piano Container --- */
     #piano-app .pa-piano-outer {     #piano-app .pa-piano-outer {
       position: relative;       position: relative;
Zeile 195: Zeile 298:
       border: 1px solid var(--pa-border);       border: 1px solid var(--pa-border);
       width: 100%;       width: 100%;
 +      overflow: hidden;
     }     }
  
Zeile 201: Zeile 305:
       overflow-y: hidden;       overflow-y: hidden;
       padding: 10px 10px 14px;       padding: 10px 10px 14px;
 +      /* Scrollbar ausblenden – Navigation nur via Slider */
 +      scrollbar-width: none;
 +      -ms-overflow-style: none;
 +    }
 +
 +    #piano-app .pa-piano-scroll::-webkit-scrollbar {
 +      display: none;
     }     }
  
Zeile 290: Zeile 401:
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Source+Sans+Pro:wght@400;600&display=swap">   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Source+Sans+Pro:wght@400;600&display=swap">
  
-  <h1 class="pa-title">🎹 Interaktive Klaviatur</h1><br></br>+  <h1 class="pa-title">🎹 Interaktive Klaviatur</h1> 
 +<br></br>
  
   <!-- Controls -->   <!-- Controls -->
Zeile 312: Zeile 424:
  
   <!-- Staff -->   <!-- Staff -->
-  <div class="pa-staff-wrap"> +  <div class="pa-staff-outer"> 
-    <div class="pa-staff-label" id="pa-clef-label">Violinschlüssel (Sopran / Alt)</div> +    <div class="pa-staff-resize-bar"> 
-    <svg class="pa-staff-svg" id="pa-staff-svg" viewBox="0 0 700 90" preserveAspectRatio="xMinYMid meet"></svg>+      <div class="pa-staff-label" id="pa-clef-label">Violinschlüssel (Sopran / Alt)</div> 
 +      <div class="pa-staff-size-wrap"> 
 +        <span>Größe:</span> 
 +        <input type="range" id="pa-staff-size" min="60" max="200" value="100" 
 +               oninput="paResizeStaff(this.value)"> 
 +      </div> 
 +    </div> 
 +    <div class="pa-staff-wrap" id="pa-staff-wrap"> 
 +      <div class="pa-clef-badge" id="pa-clef-badge">𝄞</div> 
 +      <svg class="pa-staff-svg" id="pa-staff-svg" 
 +           viewBox="0 0 600 80" preserveAspectRatio="xMidYMid meet"></svg
 +    </div>
   </div>   </div>
  
   <!-- Piano -->   <!-- Piano -->
   <div class="pa-piano-outer" id="pa-piano-outer">   <div class="pa-piano-outer" id="pa-piano-outer">
-    <div class="pa-piano-scroll">+    <div class="pa-octave-bar"> 
 +      <span>◀</span> 
 +      <input type="range" id="pa-octave-slider" min="0" max="100" value="0" 
 +             oninput="paScrollPiano(this.value)"> 
 +      <span>▶</span> 
 +    </div> 
 +    <div class="pa-piano-scroll" id="pa-piano-scroll">
       <div class="pa-keys" id="pa-keys"></div>       <div class="pa-keys" id="pa-keys"></div>
     </div>     </div>
Zeile 444: Zeile 573:
       const totalWidth = whiteKeys.length * WHITE_W;       const totalWidth = whiteKeys.length * WHITE_W;
       keysContainer.style.width = totalWidth + 'px';       keysContainer.style.width = totalWidth + 'px';
 +      // Beim Start ganz nach rechts scrollen (hohe Töne sichtbar)
 +setTimeout(() => {
 +  const scrollEl = document.getElementById('pa-piano-scroll');
 +  scrollEl.scrollLeft = scrollEl.scrollWidth;
 +}, 0);
  
       // Weiße Tasten       // Weiße Tasten
Zeile 517: Zeile 651:
     let currentVoice = 'frauen'; // 'frauen' | 'maenner'     let currentVoice = 'frauen'; // 'frauen' | 'maenner'
  
-    // SVG-Koordinaten des Notensystems +    // SVG viewBox"0 0 600 80
-    // Notenlinien beginnen bei x=CLEF_WIDTH, SVG viewBox="0 0 700 120+    // Notenlinien: x von 0 bis 600, zentriert vertikal 
-    const CLEF_WIDTH = 60;   // Platz links für den Schlüssel (INNERHALB der Notenlinien) +    const STAFF_X1    0
-    const STAFF_LEFT CLEF_WIDTH+    const STAFF_X2    600
-    const STAFF_RIGHT 680+    const STAFF_TOP   16  // y der 1. (obersten) Notenlinie 
-    const STAFF_TOP = 20   // y der 1. (obersten) Notenlinie +    const LINE_DIST   10  // Abstand zwischen Notenlinien
-    const LINE_DIST = 12   // Abstand zwischen zwei Notenlinien (px)+
  
-    function staffY(i) { // i=0 oben, i=4 unten +    function staffY(i) { return STAFF_TOP + i * LINE_DIST; } // i=0 oben … 4 unten
-      return STAFF_TOP + i * LINE_DIST; +
-    }+
  
     // -------------------------------------------------------     // -------------------------------------------------------
     // Diatonische Stufenberechnung     // Diatonische Stufenberechnung
-    // +    // Kreuz: Stammton = nächst tieferer (Cis → C-Position
-    // Jeder MIDI-Ton bekommt eine "diatonische Stufe": +    // Be   Stammton = nächst höherer  (Des → D-Position)
-    //   Stufe 0 = unterste Notenlinie des jeweiligen Schlüssels +
-    //   Stufe 1 = erster Zwischenraum, Stufe 2 = zweite Linie, ... +
-    // +
-    // Für ♯-Tasten gilt:  Stammton = nächster tieferer Stammton  (Cis + #+
-    // Für ♭-Tasten gilt Stammton = nächster höherer Stammton   (Des + b) +
-    // +
-    // Violinschlüssel: unterste Linie = E4 (MIDI 64) +
-    // Bassschlüssel:   unterste Linie = G2 (MIDI 43)+
     // -------------------------------------------------------     // -------------------------------------------------------
- 
-    // Chromatisch → diatonische Stufe innerhalb einer Oktave (C=0 … H=6) 
-    // Für Kreuz: wir runden AB (z.B. Cis → 0 wie C) 
-    // Für Be:    wir runden AUF (z.B. Des → 1 wie D) 
     function chromaToDia(chroma, useFlat) {     function chromaToDia(chroma, useFlat) {
-      // chroma: 0..11 +      const sharp = [0,0,1,1,2,3,3,4,4,5,5,6]; 
-      // Ohne Vorzeichen-Logik: C=0,D=1,E=2,F=3,G=4,A=5,H=6 +      const flat  = [0,1,1,2,2,3,4,4,5,5,6,6]; 
-      // Chromatisch:           0,2,4,5,7,9,11 sind Stamm +      return useFlat ? flat[chroma] : sharp[chroma];
-      //                        1,3,6,8,10 sind alteriert +
-      const diaMap = [0,0,1,1,2,3,3,4,4,5,5,6]; // Kreuz-Logik (abrunden) +
-      const diaMapFlat = [0,1,1,2,2,3,4,4,5,5,6,6]; // Be-Logik (aufrunden) +
-      return useFlat ? diaMapFlat[chroma] : diaMap[chroma];+
     }     }
- 
     function midiToDiaGlobal(midi, useFlat) {     function midiToDiaGlobal(midi, useFlat) {
-      const oct = Math.floor(midi / 12)+      return Math.floor(midi / 12) * 7 + chromaToDia(midi % 12, useFlat);
-      const chroma = midi % 12; +
-      return oct * 7 + chromaToDia(chroma, useFlat);+
     }     }
- 
     function midiToDiatonicStep(midi, clef) {     function midiToDiatonicStep(midi, clef) {
       const useFlat = (currentAccidental === 'flat');       const useFlat = (currentAccidental === 'flat');
-      const bassMidi   = 43; // G2 – unterste Linie Bassschlüssel +      const ref = (clef === 'violin') ? 64 43// E4 / G2
-      const violinMidi = 64; // E4 – unterste Linie Violinschlüssel +
-      const ref = (clef === 'violin') ? violinMidi bassMidi;+
       return midiToDiaGlobal(midi, useFlat) - midiToDiaGlobal(ref, false);       return midiToDiaGlobal(midi, useFlat) - midiToDiaGlobal(ref, false);
     }     }
- 
     function stepToY(step) {     function stepToY(step) {
-      // step 0 = unterste Linie = staffY(4) 
-      // step 2 = zweite Linie von unten = staffY(3), usw. 
       return staffY(4) - step * (LINE_DIST / 2);       return staffY(4) - step * (LINE_DIST / 2);
     }     }
- +    function isAltered(midi) {
-    function isAltered(midi) { // schwarze Taste?+
       return [1,3,6,8,10].includes(midi % 12);       return [1,3,6,8,10].includes(midi % 12);
     }     }
  
     // -------------------------------------------------------     // -------------------------------------------------------
-    // Notenlinien zeichnen+    // Schlüssel-Badge aktualisieren (kein SVG-Pfad mehr!) 
 +    // ------------------------------------------------------- 
 +    function updateClefBadge(clef) { 
 +      const badge = document.getElementById('pa-clef-badge'); 
 +      if (clef === 'violin') { 
 +        badge.textContent = '𝄞'; 
 +        badge.style.fontSize = '38px'; 
 +        badge.style.paddingBottom = '4px'; 
 +      } else { 
 +        badge.textContent = '𝄢'; 
 +        badge.style.fontSize = '28px'; 
 +        badge.style.paddingBottom = '0'; 
 +      } 
 +    } 
 + 
 +    // ------------------------------------------------------- 
 +    // Notenzeile zeichnen
     // -------------------------------------------------------     // -------------------------------------------------------
     function drawStaff(svg) {     function drawStaff(svg) {
Zeile 588: Zeile 709:
         const y = staffY(i);         const y = staffY(i);
         const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');         const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
-        line.setAttribute('x1', 0); +        line.setAttribute('x1', STAFF_X1); line.setAttribute('x2', STAFF_X2); 
-        line.setAttribute('x2', STAFF_RIGHT); +        line.setAttribute('y1', y);        line.setAttribute('y2', y); 
-        line.setAttribute('y1', y); +        line.setAttribute('stroke', '#444'); line.setAttribute('stroke-width', '1.2');
-        line.setAttribute('y2', y); +
-        line.setAttribute('stroke', '#333'); +
-        line.setAttribute('stroke-width', '1.2');+
         svg.appendChild(line);         svg.appendChild(line);
       }       }
     }     }
  
-    // ------------------------------------------------------- 
-    // Schlüssel zeichnen – beide als SVG-Pfade, exakt auf den Linien 
-    // ------------------------------------------------------- 
-    function drawClef(svg, clef) { 
-      if (clef === 'violin') { 
-        // Violinschlüssel: 
-        // - Die geschwungene Spirale umschließt die 2. Linie von unten (= G-Linie = staffY(3)) 
-        // - Der Bogen reicht von über staffY(0) bis unter staffY(4) 
-        // Wir zeichnen einen stilisierten, aber korrekten Violinschlüssel als Pfad. 
-        const gL = staffY(3); // G-Linie (2. von unten) 
-        const top = staffY(0) - 6; 
-        const bot = staffY(4) + 8; 
-        const mx  = 28; // x-Mitte des Schlüssels 
- 
-        const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); 
-        // Hauptlinie (senkrechter Strich) 
-        const stem = document.createElementNS('http://www.w3.org/2000/svg', 'line'); 
-        stem.setAttribute('x1', mx); stem.setAttribute('x2', mx); 
-        stem.setAttribute('y1', top); stem.setAttribute('y2', bot); 
-        stem.setAttribute('stroke', '#222'); stem.setAttribute('stroke-width', '2'); 
-        g.appendChild(stem); 
- 
-        // Obere Schlaufe (umschlingt G-Linie) 
-        const loop = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 
-        const d = [ 
-          `M ${mx} ${gL + 8}`, 
-          `C ${mx-18} ${gL+8}, ${mx-18} ${gL-14}, ${mx} ${gL-14}`, 
-          `C ${mx+12} ${gL-14}, ${mx+14} ${gL+2}, ${mx+6}  ${gL+10}`, 
-          `C ${mx+0}  ${gL+16}, ${mx-10} ${gL+14}, ${mx-8}  ${gL+8}` 
-        ].join(' '); 
-        loop.setAttribute('d', d); 
-        loop.setAttribute('stroke', '#222'); 
-        loop.setAttribute('stroke-width', '2'); 
-        loop.setAttribute('fill', 'none'); 
-        loop.setAttribute('stroke-linecap', 'round'); 
-        loop.setAttribute('stroke-linejoin', 'round'); 
-        g.appendChild(loop); 
- 
-        // Unterer Schweif 
-        const tail = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 
-        const dt = [ 
-          `M ${mx} ${gL + 10}`, 
-          `C ${mx+2} ${gL+22}, ${mx+16} ${gL+22}, ${mx+14} ${bot - 4}`, 
-          `C ${mx+12} ${bot+2}, ${mx-4} ${bot+4}, ${mx-6} ${bot-2}` 
-        ].join(' '); 
-        tail.setAttribute('d', dt); 
-        tail.setAttribute('stroke', '#222'); 
-        tail.setAttribute('stroke-width', '2'); 
-        tail.setAttribute('fill', 'none'); 
-        tail.setAttribute('stroke-linecap', 'round'); 
-        g.appendChild(tail); 
- 
-        svg.appendChild(g); 
- 
-      } else { 
-        // Bassschlüssel: 
-        // - Ausgangspunkt (Kehle / "F") liegt auf der F-Linie = 2. Linie von oben = staffY(1) 
-        // - Die zwei Punkte umschließen die F-Linie: einer darüber, einer darunter 
-        // - Der Schweif läuft von staffY(1) nach unten bis ca. staffY(4) 
-        const fY   = staffY(1);  // F-Linie (2. Linie von oben) 
-        const g    = document.createElementNS('http://www.w3.org/2000/svg', 'g'); 
-        const mx   = 22;         // x-Mittelpunkt 
- 
-        // Geschwungener Bogen: startet auf der F-Linie, biegt nach rechts-oben, 
-        // dann nach rechts-unten und endet im Schweif 
-        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 
-        const d = [ 
-          `M ${mx}    ${fY}`, 
-          // Aufwärtsbogen zur oberen Rundung (bleibt INNERHALB des Systems) 
-          `C ${mx+2}  ${fY - LINE_DIST*0.8}, ${mx+24} ${fY - LINE_DIST*0.8}, ${mx+22} ${fY + LINE_DIST*0.2}`, 
-          // Bogen nach unten-rechts in die Mitte 
-          `C ${mx+20} ${fY + LINE_DIST*0.8}, ${mx+8}  ${fY + LINE_DIST*1.2}, ${mx+4}  ${fY + LINE_DIST*1.8}`, 
-          // Schweif nach unten-links bis unterste Linie 
-          `C ${mx}    ${fY + LINE_DIST*2.6}, ${mx-6}  ${fY + LINE_DIST*3.0}, ${mx-2}  ${fY + LINE_DIST*3.6}` 
-        ].join(' '); 
-        path.setAttribute('d', d); 
-        path.setAttribute('stroke', '#222'); 
-        path.setAttribute('stroke-width', '2.5'); 
-        path.setAttribute('fill', 'none'); 
-        path.setAttribute('stroke-linecap', 'round'); 
-        path.setAttribute('stroke-linejoin', 'round'); 
-        g.appendChild(path); 
- 
-        // Zwei Punkte: oberhalb und unterhalb der F-Linie (staffY(1)) 
-        const dotX = mx + 26; 
-        const gap  = LINE_DIST * 0.55; 
-        [fY - gap, fY + gap].forEach(function(dy) { 
-          const dot = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 
-          dot.setAttribute('cx', dotX); 
-          dot.setAttribute('cy', dy); 
-          dot.setAttribute('r', '3'); 
-          dot.setAttribute('fill', '#222'); 
-          g.appendChild(dot); 
-        }); 
- 
-        svg.appendChild(g); 
-      } 
-    } 
- 
-    // ------------------------------------------------------- 
-    // Note zeichnen 
-    // ------------------------------------------------------- 
     function drawNote(midi) {     function drawNote(midi) {
-      const svg = document.getElementById('pa-staff-svg');+      const svg  = document.getElementById('pa-staff-svg');
       const clef = currentVoice === 'frauen' ? 'violin' : 'bass';       const clef = currentVoice === 'frauen' ? 'violin' : 'bass';
       drawStaff(svg);       drawStaff(svg);
-      drawClef(svg, clef); 
  
       if (midi == null) return;       if (midi == null) return;
  
       const step  = midiToDiatonicStep(midi, clef);       const step  = midiToDiatonicStep(midi, clef);
-      const noteX = STAFF_LEFT + (STAFF_RIGHT - STAFF_LEFT) / 2;+      const noteX = STAFF_X2 / 2;
       const noteY = stepToY(step);       const noteY = stepToY(step);
       const rx = 7, ry = 5;       const rx = 7, ry = 5;
  
-      // Hilfslinien unterhalb des Systems (step < 0, gerade Stufen = Linien)+      // Hilfslinien unterhalb (step < 0, nur gerade Stufen = Linien)
       for (let s = -2; s >= step; s -= 2) {       for (let s = -2; s >= step; s -= 2) {
-        const ly = stepToY(s); +        const hl = document.createElementNS('http://www.w3.org/2000/svg', 'line'); 
-        const aux = document.createElementNS('http://www.w3.org/2000/svg', 'line'); +        hl.setAttribute('x1', noteX - rx - 5); hl.setAttribute('x2', noteX + rx + 5); 
-        aux.setAttribute('x1', noteX - rx - 5); aux.setAttribute('x2', noteX + rx + 5); +        hl.setAttribute('y1', stepToY(s));     hl.setAttribute('y2', stepToY(s)); 
-        aux.setAttribute('y1', ly); aux.setAttribute('y2', ly); +        hl.setAttribute('stroke', '#444'); hl.setAttribute('stroke-width', '1.2'); 
-        aux.setAttribute('stroke', '#333'); aux.setAttribute('stroke-width', '1.2'); +        svg.appendChild(hl);
-        svg.appendChild(aux);+
       }       }
-      // Hilfslinien oberhalb des Systems (step > 8)+      // Hilfslinien oberhalb (step > 8)
       for (let s = 10; s <= step; s += 2) {       for (let s = 10; s <= step; s += 2) {
-        const ly = stepToY(s); +        const hl = document.createElementNS('http://www.w3.org/2000/svg', 'line'); 
-        const aux = document.createElementNS('http://www.w3.org/2000/svg', 'line'); +        hl.setAttribute('x1', noteX - rx - 5); hl.setAttribute('x2', noteX + rx + 5); 
-        aux.setAttribute('x1', noteX - rx - 5); aux.setAttribute('x2', noteX + rx + 5); +        hl.setAttribute('y1', stepToY(s));     hl.setAttribute('y2', stepToY(s)); 
-        aux.setAttribute('y1', ly); aux.setAttribute('y2', ly); +        hl.setAttribute('stroke', '#444'); hl.setAttribute('stroke-width', '1.2'); 
-        aux.setAttribute('stroke', '#333'); aux.setAttribute('stroke-width', '1.2'); +        svg.appendChild(hl);
-        svg.appendChild(aux);+
       }       }
  
       // Notenkopf       // Notenkopf
       const ellipse = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');       const ellipse = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
-      ellipse.setAttribute('cx', noteX); +      ellipse.setAttribute('cx', noteX); ellipse.setAttribute('cy', noteY); 
-      ellipse.setAttribute('cy', noteY); +      ellipse.setAttribute('rx', rx);    ellipse.setAttribute('ry', ry);
-      ellipse.setAttribute('rx', rx); +
-      ellipse.setAttribute('ry', ry);+
       ellipse.setAttribute('fill', '#e94560');       ellipse.setAttribute('fill', '#e94560');
-      ellipse.setAttribute('transform', `rotate(-15, ${noteX}, ${noteY})`);+      ellipse.setAttribute('transform', `rotate(-15,${noteX},${noteY})`);
       svg.appendChild(ellipse);       svg.appendChild(ellipse);
  
-      // Hals (Notenhals nach oben wenn unter Mittellinie, sonst nach unten) +      // Notenhals 
-      const stemUp  = step < 4; +      const stemUp = step < 4; 
-      const stemX   = stemUp ? noteX + rx - 1 : noteX - rx + 1+      const stemX  = stemUp ? noteX + rx - 1 : noteX - rx + 1; 
-      const stemY1  = noteY + (stemUp ? -ry : ry); +      const stem   = document.createElementNS('http://www.w3.org/2000/svg', 'line');
-      const stemY2  = noteY + (stemUp ? -38 : 38)+
-      const stem    = document.createElementNS('http://www.w3.org/2000/svg', 'line');+
       stem.setAttribute('x1', stemX); stem.setAttribute('x2', stemX);       stem.setAttribute('x1', stemX); stem.setAttribute('x2', stemX);
-      stem.setAttribute('y1', stemY1); stem.setAttribute('y2', stemY2);+      stem.setAttribute('y1', noteY + (stemUp ? -ry : ry)); 
 +      stem.setAttribute('y2', noteY + (stemUp ? -38 : 38));
       stem.setAttribute('stroke', '#222'); stem.setAttribute('stroke-width', '1.5');       stem.setAttribute('stroke', '#222'); stem.setAttribute('stroke-width', '1.5');
       svg.appendChild(stem);       svg.appendChild(stem);
Zeile 755: Zeile 765:
       // Vorzeichen       // Vorzeichen
       if (isAltered(midi)) {       if (isAltered(midi)) {
-        const accText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); +        const acc = document.createElementNS('http://www.w3.org/2000/svg', 'text'); 
-        accText.setAttribute('x', noteX - rx - 10); +        acc.setAttribute('x', noteX - rx - 10); acc.setAttribute('y', noteY + 5); 
-        accText.setAttribute('y', noteY + 5); +        acc.setAttribute('font-size', '15'); 
-        accText.setAttribute('font-size', '15'); +        acc.setAttribute('fill', '#333'); 
-        accText.setAttribute('fill', '#333'); +        acc.setAttribute('text-anchor', 'middle'); 
-        accText.setAttribute('text-anchor', 'middle'); +        acc.textContent = currentAccidental === 'sharp' ? '♯' : '♭'; 
-        accText.textContent = currentAccidental === 'sharp' ? '♯' : '♭'; +        svg.appendChild(acc);
-        svg.appendChild(accText);+
       }       }
  
-      // Notenname als Label rechts neben dem Kopf +      // Notenname-Label 
-      const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); +      const lbl = document.createElementNS('http://www.w3.org/2000/svg', 'text'); 
-      label.setAttribute('x', noteX + rx + 16); +      lbl.setAttribute('x', noteX + rx + 16); lbl.setAttribute('y', noteY + 4); 
-      label.setAttribute('y', noteY + 4); +      lbl.setAttribute('font-size', '11'); 
-      label.setAttribute('font-size', '11'); +      lbl.setAttribute('fill', '#666'); 
-      label.setAttribute('fill', '#555'); +      lbl.setAttribute('font-family', 'sans-serif'); 
-      label.setAttribute('font-family', 'sans-serif'); +      lbl.textContent = getNoteName(midi); 
-      label.textContent = getNoteName(midi); +      svg.appendChild(lbl);
-      svg.appendChild(label);+
     }     }
  
Zeile 780: Zeile 788:
       const svg = document.getElementById('pa-staff-svg');       const svg = document.getElementById('pa-staff-svg');
       drawStaff(svg);       drawStaff(svg);
-      drawClef(svg, 'violin');+      updateClefBadge('violin');
     })();     })();
  
Zeile 790: Zeile 798:
       document.getElementById('pa-btn-frauen').classList.toggle('pa-active', voice === 'frauen');       document.getElementById('pa-btn-frauen').classList.toggle('pa-active', voice === 'frauen');
       document.getElementById('pa-btn-maenner').classList.toggle('pa-active', voice === 'maenner');       document.getElementById('pa-btn-maenner').classList.toggle('pa-active', voice === 'maenner');
 +      const clef = voice === 'frauen' ? 'violin' : 'bass';
       document.getElementById('pa-clef-label').textContent =       document.getElementById('pa-clef-label').textContent =
-        voice === 'frauen' +        voice === 'frauen' ? 'Violinschlüssel (Sopran / Alt)' : 'Bassschlüssel (Tenor / Bass)'; 
-          ? 'Violinschlüssel (Sopran / Alt)' +      updateClefBadge(clef);
-          : 'Bassschlüssel (Tenor / Bass)'; +
- +
-      const svg = document.getElementById('pa-staff-svg'); +
-      drawStaff(svg); +
-      drawClef(svg, voice === 'frauen' ? 'violin' : 'bass'); +
-      // letzte Note neu zeichnen+
       if (lastPlayedMidi != null) drawNote(lastPlayedMidi);       if (lastPlayedMidi != null) drawNote(lastPlayedMidi);
 +      else drawStaff(document.getElementById('pa-staff-svg'));
     };     };
  
Zeile 809: Zeile 813:
       document.getElementById('pa-btn-sharp').classList.toggle('pa-active', acc === 'sharp');       document.getElementById('pa-btn-sharp').classList.toggle('pa-active', acc === 'sharp');
       document.getElementById('pa-btn-flat').classList.toggle('pa-active', acc === 'flat');       document.getElementById('pa-btn-flat').classList.toggle('pa-active', acc === 'flat');
-      // Tastenbeschriftungen neu rendern 
       buildKeyboard();       buildKeyboard();
-      // letzte Note neu zeichnen 
       if (lastPlayedMidi != null) drawNote(lastPlayedMidi);       if (lastPlayedMidi != null) drawNote(lastPlayedMidi);
     };     };
 +
     // =====================================================     // =====================================================
 +    // 5c. Notenzeilen-Größe anpassen
 +    // =====================================================
 +    window.paResizeStaff = function(val) {
 +  const wrap = document.getElementById('pa-staff-wrap');
 +  const svg = document.getElementById('pa-staff-svg');
 +  const badge = document.getElementById('pa-clef-badge');
 +  // Skalierungsfaktor (100 = Standard)
 +  const scale = val / 100;
 +  // Padding anpassen
 +  const padding = Math.round(16 * scale);
 +  wrap.style.padding = padding + 'px 20px';
 +  // SVG-Höhe skalieren (Basis: 80px)
 +  const svgHeight = Math.round(80 * scale);
 +  svg.style.height = svgHeight + 'px';
 +  // Badge-Größe anpassen
 +  const bSize = Math.round(52 * scale);
 +  badge.style.width = bSize + 'px';
 +  badge.style.height = bSize + 'px';
 +  badge.style.fontSize = Math.round(bSize * 0.73) + 'px';
 +};
 +
 +    // =====================================================
 +    // 5d. Klaviatur-Scroll per Slider (Oktaven-Navigation)
 +    // =====================================================
 +    window.paScrollPiano = function(val) {
 +      const scrollEl = document.getElementById('pa-piano-scroll');
 +      const maxScroll = scrollEl.scrollWidth - scrollEl.clientWidth;
 +      scrollEl.scrollLeft = Math.round((1 - val / 100) * maxScroll);
 +    };
 +
 +    // Slider-Position beim natürlichen Scrollen (Maus/Touch) synchronisieren
 +    document.getElementById('pa-piano-scroll').addEventListener('scroll', function() {
 +      const scrollEl = this;
 +      const maxScroll = scrollEl.scrollWidth - scrollEl.clientWidth;
 +      if (maxScroll <= 0) return;
 +      const pct = Math.round((1 - scrollEl.scrollLeft / maxScroll) * 100);
 +      document.getElementById('pa-octave-slider').value = pct;
 +    });
     window.paToggleFullscreen = function() {     window.paToggleFullscreen = function() {
       const app = document.getElementById('piano-app');       const app = document.getElementById('piano-app');
Zeile 838: Zeile 879:
   </script>   </script>
 </div> </div>
 +
 </html> </html>
tools/piano.1774793632.txt.gz · Zuletzt geändert: von Eric Weber