diff --git a/src/views/detail.js b/src/views/detail.js index 096d60e..37f9ce9 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -119,36 +119,25 @@ export async function loadArtistDetail(artist) { const _bgCover = _firstAlbums[0] ? coverUrl(_firstAlbums[0].coverArt, 512) : null; const bgSrc = img || _bgCover; - // Gate reveal on pfp + bg + color all ready — nothing shows until everything is set - let _pfpReady = false, _bgReady = false, _colorReady = false; - let _heroRgb = null; - + // Reveal when images are ready; color tint fades in after (non-blocking) + let _pfpReady = false, _bgReady = false; const _revealHero = () => { - if(!_pfpReady || !_bgReady || !_colorReady) return; - if(_heroRgb) { - const [r, g, b] = _heroRgb; - hero.querySelector('.artist-hero-tint')?.remove(); - 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.25s ease'; - hero.style.opacity = '1'; - }); + if(!_pfpReady || !_bgReady) return; + requestAnimationFrame(() => { hero.style.transition = 'opacity 0.25s ease'; hero.style.opacity = '1'; }); }; - // Preload bg — only set background-image after it's in cache if(bgSrc) { hero.style.background = ''; const bgEl = document.getElementById('artist-hero-bg'); - const _bgImg = new Image(); - _bgImg.onload = _bgImg.onerror = () => { - if(bgEl) bgEl.style.backgroundImage = `url('${bgSrc}')`; - _bgReady = true; _revealHero(); - }; - _bgImg.src = bgSrc; + if(bgSrc === img) { + // same URL as pfp — browser cache will have it when pfp loads, no extra request + bgEl.style.backgroundImage = `url('${bgSrc}')`; + _bgReady = true; + } else { + const _bgPreload = new Image(); + _bgPreload.onload = _bgPreload.onerror = () => { bgEl.style.backgroundImage = `url('${bgSrc}')`; _bgReady = true; _revealHero(); }; + _bgPreload.src = bgSrc; + } } else { _bgReady = true; } @@ -163,13 +152,19 @@ export async function loadArtistDetail(artist) { pfpImg.src = img; pfpWrap.prepend(pfpImg); document.getElementById('artist-hero-pfp-initial')?.style.setProperty('display', 'none'); - } else { - _pfpReady = true; - } - _extractColor(img).then(rgb => { _heroRgb = rgb; _colorReady = true; _revealHero(); }); - } else { - _pfpReady = true; _colorReady = true; _revealHero(); - } + } else { _pfpReady = true; } + // color tint is non-blocking — fades in via CSS whenever it's ready + _extractColor(img).then(rgb => { + if(!rgb) return; + const [r, g, b] = rgb; + hero.querySelector('.artist-hero-tint')?.remove(); + 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;opacity:0;transition:opacity 0.6s ease`; + if(overlay) hero.insertBefore(tint, overlay); + requestAnimationFrame(() => { tint.style.opacity = '1'; }); + }); + } else { _pfpReady = true; _revealHero(); } // ── Bio ── const info = infoRes.status === 'fulfilled' ? infoRes.value?.artistInfo2 : null;