fix: hero waits for pfp + bg + color before revealing — no more incremental flicker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-06-07 22:36:31 +02:00
1 parent 0a3c72e2fd
commit 28de177381
1 file changed
+35 -21
+35 -21
View File
@@ -119,16 +119,39 @@ export async function loadArtistDetail(artist) {
const _bgCover = _firstAlbums[0] ? coverUrl(_firstAlbums[0].coverArt, 512) : null; const _bgCover = _firstAlbums[0] ? coverUrl(_firstAlbums[0].coverArt, 512) : null;
const bgSrc = img || _bgCover; const bgSrc = img || _bgCover;
if(bgSrc) { // Gate reveal on pfp + bg + color all ready — nothing shows until everything is set
hero.style.background = ''; let _pfpReady = false, _bgReady = false, _colorReady = false;
const bgEl = document.getElementById('artist-hero-bg'); let _heroRgb = null;
if(bgEl) bgEl.style.backgroundImage = `url('${bgSrc}')`;
}
const _revealHero = () => requestAnimationFrame(() => { 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.transition = 'opacity 0.25s ease';
hero.style.opacity = '1'; 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;
} else {
_bgReady = true;
}
if(img) { if(img) {
const pfpWrap = document.getElementById('artist-hero-pfp-wrap'); const pfpWrap = document.getElementById('artist-hero-pfp-wrap');
@@ -136,25 +159,16 @@ export async function loadArtistDetail(artist) {
const pfpImg = document.createElement('img'); const pfpImg = document.createElement('img');
pfpImg.className = 'artist-hero-pfp'; pfpImg.className = 'artist-hero-pfp';
pfpImg.alt = ''; pfpImg.alt = '';
pfpImg.onload = _revealHero; pfpImg.onload = pfpImg.onerror = () => { _pfpReady = true; _revealHero(); };
pfpImg.onerror = _revealHero;
pfpImg.src = img; pfpImg.src = img;
pfpWrap.prepend(pfpImg); pfpWrap.prepend(pfpImg);
document.getElementById('artist-hero-pfp-initial')?.style.setProperty('display', 'none'); document.getElementById('artist-hero-pfp-initial')?.style.setProperty('display', 'none');
}
// color tint — async, fades in via CSS so it never pops
_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.5s ease`;
if(overlay) hero.insertBefore(tint, overlay);
requestAnimationFrame(() => { tint.style.opacity = '1'; });
});
} else { } else {
_revealHero(); _pfpReady = true;
}
_extractColor(img).then(rgb => { _heroRgb = rgb; _colorReady = true; _revealHero(); });
} else {
_pfpReady = true; _colorReady = true; _revealHero();
} }
// ── Bio ── // ── Bio ──