From 42fe5d802bb10f50953933c390f03965aab99740 Mon Sep 17 00:00:00 2001 From: Honey Date: Sun, 7 Jun 2026 22:19:37 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20hero=20fades=20in=20complete=20=E2=80=94?= =?UTF-8?q?=20no=20more=20incremental=20load=20flicker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/views/detail.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/views/detail.js b/src/views/detail.js index 677c7cf..f7ff028 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -69,6 +69,8 @@ export async function loadArtistDetail(artist) { const simEl = document.getElementById('artist-similar-section'); const tsEl = document.getElementById('artist-topsongs-section'); const albEl = document.getElementById('artist-albums-section'); + hero.style.opacity = '0'; + hero.style.transition = ''; hero.style.background = 'linear-gradient(135deg, var(--surface2), var(--surface))'; hero.querySelector('.artist-hero-img')?.remove(); hero.querySelector('.artist-hero-tint')?.remove(); @@ -111,12 +113,18 @@ export async function loadArtistDetail(artist) { ]) : [{ status: 'rejected' }, { status: 'rejected' }]; - // ── Hero ── + // ── Hero ── (await color before showing anything so it all renders at once) const img = imgRes.status === 'fulfilled' ? imgRes.value : null; const _firstAlbums = albumRes.status === 'fulfilled' ? (albumRes.value?.artist?.album || []) : []; const _bgCover = _firstAlbums[0] ? coverUrl(_firstAlbums[0].coverArt, 512) : null; - // Option 1: artist photo as background (falls back to album art) const bgSrc = img || _bgCover; + + // _extractColor loads img into canvas — warms the browser cache for the display uses below + const [rgb] = await Promise.all([ + img ? _extractColor(img) : Promise.resolve(null), + bgSrc && bgSrc !== img ? new Promise(r => { const i = new Image(); i.onload = i.onerror = r; i.src = bgSrc; }) : Promise.resolve(), + ]); + if(bgSrc) { hero.style.background = ''; const bgEl = document.getElementById('artist-hero-bg'); @@ -133,16 +141,18 @@ export async function loadArtistDetail(artist) { const initial = document.getElementById('artist-hero-pfp-initial'); if(initial) initial.style.display = 'none'; } - // Option 2: dominant color tint — async, non-blocking - _extractColor(img).then(rgb => { - if(!rgb) return; - const [r, g, b] = rgb; - const tint = document.createElement('div'); - tint.className = 'artist-hero-tint'; - tint.style.cssText = `position:absolute;inset:0;background:linear-gradient(to right,rgba(${r},${g},${b},.55) 0%,rgba(${r},${g},${b},.25) 55%,rgba(${r},${g},${b},.6) 100%);pointer-events:none`; - if(overlay) hero.insertBefore(tint, overlay); - }); } + if(rgb) { + const [r, g, b] = rgb; + const tint = document.createElement('div'); + tint.className = 'artist-hero-tint'; + tint.style.cssText = `position:absolute;inset:0;background:linear-gradient(to right,rgba(${r},${g},${b},.55) 0%,rgba(${r},${g},${b},.25) 55%,rgba(${r},${g},${b},.6) 100%);pointer-events:none`; + if(overlay) hero.insertBefore(tint, overlay); + } + requestAnimationFrame(() => { + hero.style.transition = 'opacity 0.3s ease'; + hero.style.opacity = '1'; + }); // ── Bio ── const info = infoRes.status === 'fulfilled' ? infoRes.value?.artistInfo2 : null;