fix: solid cover-color NP background via canvas extraction

Replace saturated blur filter with actual dominant-color extraction:
fetch cover as blob (avoids canvas CORS taint), draw on 24x24 canvas,
find most vivid pixel, darken to ~28% for a moody solid background.
Blurred bg layers hidden when mode is active. Transitions smoothly
between songs via CSS transition on background.
Renamed setting label to "Einfarbiger Player-Hintergrund".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-28 00:36:32 +02:00
1 parent 20545f71be
commit 9ab3dab508
3 files changed
+47 -10

No files matched your search

+1 -4
View File
@@ -664,10 +664,7 @@
</h3>
<div id="theme-picker" class="theme-picker"></div>
<div class="edit-pl-toggle-row" style="margin-top:14px">
<div>
<span>Dynamische Hintergrundfarbe</span>
<div class="hint" style="margin-top:2px">Jetzt-Läuft-Hintergrund nach Cover-Hauptfarbe</div>
</div>
<span>Einfarbiger Player-Hintergrund</span>
<div id="s-cover-color-bg" class="toggle" onclick="this.classList.toggle('on')"></div>
</div>
</div>
+43 -2
View File
@@ -484,9 +484,49 @@ export function updatePlayerUI(s, _skipMediaSession = false) {
if (_npCv && _npCv !== _npBgLastUrl) { const _pre = new Image(); _pre.src = _npCv; }
}
// Apply/remove dynamic-color mode based on current setting.
// ── Solid cover-color background ────────────────────────────────────────────
// Fetch the cover as a blob (bypasses canvas CORS taint), draw on a tiny
// canvas, find the most vivid pixel, darken it to a moody solid background.
let _npColorActiveUrl = '';
async function _extractAndApplyColor(cv) {
if (!cv || !S.cfg?.coverColorBg) return;
if (cv === _npColorActiveUrl) return;
_npColorActiveUrl = cv;
try {
const res = await fetch(cv, { signal: AbortSignal.timeout(6000) });
const blob = await res.blob();
const burl = URL.createObjectURL(blob);
const img = await new Promise((ok, err) => {
const i = new Image(); i.onload = () => ok(i); i.onerror = err; i.src = burl;
});
URL.revokeObjectURL(burl);
if (cv !== _npColorActiveUrl) return; // superseded by next song
const c = document.createElement('canvas'); c.width = c.height = 24;
const ctx = c.getContext('2d'); ctx.drawImage(img, 0, 0, 24, 24);
const d = ctx.getImageData(0, 0, 24, 24).data;
let br = 20, bg2 = 20, bb = 40, best = 0;
for (let i = 0; i < d.length; i += 4) {
const r = d[i], g = d[i+1], b = d[i+2];
const mx = Math.max(r,g,b), mn = Math.min(r,g,b);
const sat = mx > 0 ? (mx-mn)/mx : 0;
const lum = (r+g+b)/3;
const score = sat * 2 + (lum > 30 && lum < 220 ? 1 : 0);
if (score > best) { best = score; br = r; bg2 = g; bb = b; }
}
// Darken significantly for a moody solid bg
const el = document.getElementById('np-overlay');
if (el && S.cfg?.coverColorBg)
el.style.setProperty('--np-solid', `${Math.round(br*.28)},${Math.round(bg2*.28)},${Math.round(bb*.28)}`);
} catch {}
}
export function updateNpColorMode() {
document.getElementById('np-overlay')?.classList.toggle('dynamic-color', !!S.cfg?.coverColorBg);
const el = document.getElementById('np-overlay');
if (!el) return;
const on = !!S.cfg?.coverColorBg;
el.classList.toggle('dynamic-color', on);
if (!on) { el.style.removeProperty('--np-solid'); _npColorActiveUrl = ''; }
else if (_npBgLastUrl) _extractAndApplyColor(_npBgLastUrl);
}
let _npBgCurr = 'a';
@@ -494,6 +534,7 @@ let _npBgLastUrl = '';
function _crossfadeNpBg(cv) {
if ((cv || '') === _npBgLastUrl) return;
_npBgLastUrl = cv || '';
if (S.cfg?.coverColorBg) _extractAndApplyColor(cv || '');
const doFade = () => {
const currEl = document.getElementById(`np-bg-${_npBgCurr}`);
const nextId = _npBgCurr === 'a' ? 'b' : 'a';
+3 -4
View File
@@ -590,10 +590,9 @@ button:focus{outline:none}
will-change:opacity;
z-index:0;
}
/* Dynamic cover color mode: more vivid/saturated background */
.np-overlay.dynamic-color .np-bg{
filter:blur(60px) brightness(.5) saturate(3);
}
/* Solid cover-color mode: hide blurred layers, use extracted color as background */
.np-overlay.dynamic-color{background:rgb(var(--np-solid,20,20,40));transition:background .5s ease,transform .35s cubic-bezier(.4,0,.2,1)}
.np-overlay.dynamic-color .np-bg{opacity:0!important;pointer-events:none}
.np-inner{
position:relative;z-index:1;