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:
1 parent
20545f71be
commit
9ab3dab508
3 files changed
+47
-10
No files matched your search
+1
-4
@@ -664,10 +664,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div id="theme-picker" class="theme-picker"></div>
|
<div id="theme-picker" class="theme-picker"></div>
|
||||||
<div class="edit-pl-toggle-row" style="margin-top:14px">
|
<div class="edit-pl-toggle-row" style="margin-top:14px">
|
||||||
<div>
|
<span>Einfarbiger Player-Hintergrund</span>
|
||||||
<span>Dynamische Hintergrundfarbe</span>
|
|
||||||
<div class="hint" style="margin-top:2px">Jetzt-Läuft-Hintergrund nach Cover-Hauptfarbe</div>
|
|
||||||
</div>
|
|
||||||
<div id="s-cover-color-bg" class="toggle" onclick="this.classList.toggle('on')"></div>
|
<div id="s-cover-color-bg" class="toggle" onclick="this.classList.toggle('on')"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+43
-2
@@ -484,9 +484,49 @@ export function updatePlayerUI(s, _skipMediaSession = false) {
|
|||||||
if (_npCv && _npCv !== _npBgLastUrl) { const _pre = new Image(); _pre.src = _npCv; }
|
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() {
|
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';
|
let _npBgCurr = 'a';
|
||||||
@@ -494,6 +534,7 @@ let _npBgLastUrl = '';
|
|||||||
function _crossfadeNpBg(cv) {
|
function _crossfadeNpBg(cv) {
|
||||||
if ((cv || '') === _npBgLastUrl) return;
|
if ((cv || '') === _npBgLastUrl) return;
|
||||||
_npBgLastUrl = cv || '';
|
_npBgLastUrl = cv || '';
|
||||||
|
if (S.cfg?.coverColorBg) _extractAndApplyColor(cv || '');
|
||||||
const doFade = () => {
|
const doFade = () => {
|
||||||
const currEl = document.getElementById(`np-bg-${_npBgCurr}`);
|
const currEl = document.getElementById(`np-bg-${_npBgCurr}`);
|
||||||
const nextId = _npBgCurr === 'a' ? 'b' : 'a';
|
const nextId = _npBgCurr === 'a' ? 'b' : 'a';
|
||||||
|
|||||||
+3
-4
@@ -590,10 +590,9 @@ button:focus{outline:none}
|
|||||||
will-change:opacity;
|
will-change:opacity;
|
||||||
z-index:0;
|
z-index:0;
|
||||||
}
|
}
|
||||||
/* Dynamic cover color mode: more vivid/saturated background */
|
/* Solid cover-color mode: hide blurred layers, use extracted color as background */
|
||||||
.np-overlay.dynamic-color .np-bg{
|
.np-overlay.dynamic-color{background:rgb(var(--np-solid,20,20,40));transition:background .5s ease,transform .35s cubic-bezier(.4,0,.2,1)}
|
||||||
filter:blur(60px) brightness(.5) saturate(3);
|
.np-overlay.dynamic-color .np-bg{opacity:0!important;pointer-events:none}
|
||||||
}
|
|
||||||
|
|
||||||
.np-inner{
|
.np-inner{
|
||||||
position:relative;z-index:1;
|
position:relative;z-index:1;
|
||||||
|
|||||||
Reference in new issue
Block a user