Musik-Knopf wird zum Chamäleon - passt Farben live an jede Seite an
Bisher kannte der Knopf nur 3 feste Weltfarben (dogfather/hasidog/ spicymedia). Jetzt analysiert main.js (musikKnopfFarbenAnpassen) per Canvas das tatsächliche Bild jeder einzelnen Seite - Durchschnittsfarbe plus die kräftigste Farbe darin, als Verlauf - und setzt sie live als --music-c1/--music-c2 auf den Knopf. Bildquelle in Prioritätsreihenfolge: 1) scharfes Hero-Banner-Bild, 2) Startseiten-Artwork, 3) Ambiente-Hintergrundbild der Seite. Ohne Bild fällt der Knopf sauber auf die normale Weltfarbe zurück. Läuft beim ersten Laden UND nach jedem nahtlosen Seitenwechsel (SPA-Navigation), damit die Farbe wirklich bei jeder Seite mitwechselt.
This commit is contained in:
+14
-9
@@ -1233,9 +1233,12 @@ body.seite-laedt main { opacity: .55; transition: opacity .15s ease; }
|
|||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
/* Musik-Knopf: passt sich wieder der Farbwelt der jeweiligen Seite an
|
/* Musik-Knopf: "Chamäleon" — passt sich per JS (main.js,
|
||||||
(babyblau auf Dogfather/HasiDog, rot auf Spicy-Media-Seiten) statt
|
musikKnopfFarbenAnpassen) an das tatsächliche Bild der jeweiligen Seite
|
||||||
fest halb/halb zu sein. */
|
an (Durchschnitts- + kräftigste Farbe daraus), statt nur 3 feste
|
||||||
|
Weltfarben zu kennen. --music-c1/--music-c2 werden dafür live per
|
||||||
|
Canvas-Analyse gesetzt; ohne Bild (oder solange JS noch lädt) fällt der
|
||||||
|
Knopf sauber auf die normale Weltfarbe --accent/--accent-2 zurück. */
|
||||||
.music-toggle {
|
.music-toggle {
|
||||||
width: 54px;
|
width: 54px;
|
||||||
height: 54px;
|
height: 54px;
|
||||||
@@ -1247,19 +1250,21 @@ body.seite-laedt main { opacity: .55; transition: opacity .15s ease; }
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--accent-contrast);
|
color: #fff;
|
||||||
border: 1px solid color-mix(in srgb, var(--accent) 60%, transparent);
|
text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
|
||||||
background: linear-gradient(135deg, var(--accent), var(--accent-2));
|
border: 1px solid color-mix(in srgb, var(--music-c1, var(--accent)) 60%, transparent);
|
||||||
|
background: linear-gradient(135deg, var(--music-c1, var(--accent)), var(--music-c2, var(--accent-2)));
|
||||||
box-shadow: 0 12px 30px rgba(0, 0, 0, .45),
|
box-shadow: 0 12px 30px rgba(0, 0, 0, .45),
|
||||||
0 0 24px color-mix(in srgb, var(--accent) 35%, transparent);
|
0 0 24px color-mix(in srgb, var(--music-c1, var(--accent)) 35%, transparent);
|
||||||
transition: transform .25s ease, filter .25s ease, box-shadow .25s ease;
|
transition: transform .25s ease, filter .25s ease, box-shadow .25s ease,
|
||||||
|
background 1.1s ease, border-color 1.1s ease;
|
||||||
}
|
}
|
||||||
.music-toggle:hover,
|
.music-toggle:hover,
|
||||||
.music-toggle:focus-visible {
|
.music-toggle:focus-visible {
|
||||||
transform: translateY(-2px) scale(1.05);
|
transform: translateY(-2px) scale(1.05);
|
||||||
filter: brightness(1.12);
|
filter: brightness(1.12);
|
||||||
box-shadow: 0 16px 38px rgba(0, 0, 0, .5),
|
box-shadow: 0 16px 38px rgba(0, 0, 0, .5),
|
||||||
0 0 34px color-mix(in srgb, var(--accent) 55%, transparent);
|
0 0 34px color-mix(in srgb, var(--music-c1, var(--accent)) 55%, transparent);
|
||||||
}
|
}
|
||||||
.music-widget.open .music-toggle { transform: rotate(12deg); }
|
.music-widget.open .music-toggle { transform: rotate(12deg); }
|
||||||
.music-toggle span { position: relative; z-index: 2; }
|
.music-toggle span { position: relative; z-index: 2; }
|
||||||
|
|||||||
@@ -267,6 +267,134 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
Musik-Knopf: Chamäleon-Farben
|
||||||
|
|
||||||
|
Der Knopf soll nicht nur zwischen 3 festen Weltfarben wechseln,
|
||||||
|
sondern sich an das tatsächliche Bild JEDER einzelnen Seite anpassen
|
||||||
|
— Durchschnittsfarbe plus die kräftigste (sattste) Farbe darin, als
|
||||||
|
kleiner Verlauf. Bildquelle in Prioritätsreihenfolge:
|
||||||
|
1. Das scharfe Banner-Bild im Kopfbereich (.hero-banner img)
|
||||||
|
2. Das Artwork der Startseite (.hero-artwork, CSS-Hintergrund)
|
||||||
|
3. Das Ambiente-Hintergrundbild der Seite (--page-bg am <body>)
|
||||||
|
Gibt es nichts davon (reine Text-Hero-Seiten wie Links oder Videos),
|
||||||
|
fällt der Knopf sauber auf die normale Weltfarbe zurück (siehe CSS:
|
||||||
|
var(--music-c1, var(--accent))).
|
||||||
|
|
||||||
|
Bilder sind alle selbst gehostet (gleiche Domain) — Canvas-Auslesen
|
||||||
|
funktioniert dadurch ohne CORS-Probleme.
|
||||||
|
=================================================================== */
|
||||||
|
function bildquelleErmitteln() {
|
||||||
|
const banner = document.querySelector(".hero-banner img");
|
||||||
|
if (banner && banner.getAttribute("src")) return banner;
|
||||||
|
|
||||||
|
const artwork = document.querySelector(".hero-artwork");
|
||||||
|
if (artwork) {
|
||||||
|
const hintergrund = getComputedStyle(artwork, "::before").backgroundImage;
|
||||||
|
const ersteEbene = hintergrund && hintergrund.split(",")[0];
|
||||||
|
const treffer = ersteEbene && ersteEbene.match(/url\(["']?(.*?)["']?\)/);
|
||||||
|
if (treffer && treffer[1]) return treffer[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const seitenBg = getComputedStyle(document.body).getPropertyValue("--page-bg");
|
||||||
|
const treffer2 = seitenBg && seitenBg.match(/url\(["']?(.*?)["']?\)/);
|
||||||
|
if (treffer2 && treffer2[1]) return treffer2[1];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgbNachHsl(r, g, b) {
|
||||||
|
r /= 255; g /= 255; b /= 255;
|
||||||
|
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||||
|
let h = 0, s = 0;
|
||||||
|
const l = (max + min) / 2;
|
||||||
|
if (max !== min) {
|
||||||
|
const d = max - min;
|
||||||
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
|
switch (max) {
|
||||||
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||||
|
case g: h = (b - r) / d + 2; break;
|
||||||
|
default: h = (r - g) / d + 4;
|
||||||
|
}
|
||||||
|
h *= 60;
|
||||||
|
}
|
||||||
|
return [h, s * 100, l * 100];
|
||||||
|
}
|
||||||
|
|
||||||
|
const begrenzen = (wert, min, max) => Math.min(max, Math.max(min, wert));
|
||||||
|
|
||||||
|
let musikFarbLauf = 0;
|
||||||
|
|
||||||
|
function musikKnopfFarbenAnpassen() {
|
||||||
|
const knopf = document.getElementById("music-toggle");
|
||||||
|
if (!knopf) return;
|
||||||
|
const lauf = ++musikFarbLauf;
|
||||||
|
|
||||||
|
const zuruecksetzen = () => {
|
||||||
|
knopf.style.removeProperty("--music-c1");
|
||||||
|
knopf.style.removeProperty("--music-c2");
|
||||||
|
};
|
||||||
|
|
||||||
|
const quelle = bildquelleErmitteln();
|
||||||
|
if (!quelle) {
|
||||||
|
zuruecksetzen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verarbeiten = (bild) => {
|
||||||
|
if (lauf !== musikFarbLauf) return; // zwischenzeitlich schon weitergeklickt
|
||||||
|
try {
|
||||||
|
const groesse = 28;
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = groesse;
|
||||||
|
canvas.height = groesse;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
ctx.drawImage(bild, 0, 0, groesse, groesse);
|
||||||
|
const daten = ctx.getImageData(0, 0, groesse, groesse).data;
|
||||||
|
|
||||||
|
let rSumme = 0, gSumme = 0, bSumme = 0, n = 0;
|
||||||
|
let besteSaettigung = -1, satR = 0, satG = 0, satB = 0;
|
||||||
|
for (let i = 0; i < daten.length; i += 4) {
|
||||||
|
const r = daten[i], g = daten[i + 1], b = daten[i + 2];
|
||||||
|
rSumme += r; gSumme += g; bSumme += b; n++;
|
||||||
|
const hsl = rgbNachHsl(r, g, b);
|
||||||
|
if (hsl[2] > 15 && hsl[2] < 88 && hsl[1] > besteSaettigung) {
|
||||||
|
besteSaettigung = hsl[1]; satR = r; satG = g; satB = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const r = Math.round(rSumme / n), g = Math.round(gSumme / n), b = Math.round(bSumme / n);
|
||||||
|
|
||||||
|
const [h1, s1, l1] = rgbNachHsl(r, g, b);
|
||||||
|
const farbe1 = `hsl(${Math.round(h1)}deg ${Math.round(begrenzen(s1, 42, 92))}% ${Math.round(begrenzen(l1, 40, 62))}%)`;
|
||||||
|
|
||||||
|
let farbe2;
|
||||||
|
if (besteSaettigung > 18) {
|
||||||
|
const [h2, s2, l2] = rgbNachHsl(satR, satG, satB);
|
||||||
|
farbe2 = `hsl(${Math.round(h2)}deg ${Math.round(begrenzen(s2, 50, 95))}% ${Math.round(begrenzen(l2, 42, 66))}%)`;
|
||||||
|
} else {
|
||||||
|
farbe2 = `hsl(${Math.round((h1 + 45) % 360)}deg ${Math.round(begrenzen(s1, 45, 90))}% ${Math.round(begrenzen(l1 + 8, 42, 68))}%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
knopf.style.setProperty("--music-c1", farbe1);
|
||||||
|
knopf.style.setProperty("--music-c2", farbe2);
|
||||||
|
} catch {
|
||||||
|
zuruecksetzen(); // z.B. wenn das Bild aus irgendeinem Grund nicht lesbar ist
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof quelle === "string") {
|
||||||
|
const bild = new Image();
|
||||||
|
bild.addEventListener("load", () => verarbeiten(bild));
|
||||||
|
bild.addEventListener("error", zuruecksetzen);
|
||||||
|
bild.src = quelle;
|
||||||
|
} else if (quelle.complete && quelle.naturalWidth > 0) {
|
||||||
|
verarbeiten(quelle);
|
||||||
|
} else {
|
||||||
|
quelle.addEventListener("load", () => verarbeiten(quelle), { once: true });
|
||||||
|
quelle.addEventListener("error", zuruecksetzen, { once: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
Nahtloser Seitenwechsel — damit die Musik nicht abreißt
|
Nahtloser Seitenwechsel — damit die Musik nicht abreißt
|
||||||
|
|
||||||
@@ -382,6 +510,7 @@
|
|||||||
renderHeader(); // markiert den richtigen Menüpunkt als aktiv
|
renderHeader(); // markiert den richtigen Menüpunkt als aktiv
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
document.body.classList.remove("seite-laedt");
|
document.body.classList.remove("seite-laedt");
|
||||||
|
musikKnopfFarbenAnpassen(); // Chamäleon-Knopf an die neue Seite anpassen
|
||||||
|
|
||||||
await skripteUebernehmen(dok);
|
await skripteUebernehmen(dok);
|
||||||
}
|
}
|
||||||
@@ -430,6 +559,7 @@
|
|||||||
renderFooter();
|
renderFooter();
|
||||||
renderFog();
|
renderFog();
|
||||||
renderMusicWidget();
|
renderMusicWidget();
|
||||||
|
musikKnopfFarbenAnpassen();
|
||||||
nahtloseNavigationAktivieren();
|
nahtloseNavigationAktivieren();
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user