feat: artist hero — dominant color tint + artist photo as full background
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4131907ebd
commit
e2964829bd
2 files changed
+40
-4
No files matched your search
+39
-3
@@ -71,11 +71,12 @@ export async function loadArtistDetail(artist) {
|
|||||||
const albEl = document.getElementById('artist-albums-section');
|
const albEl = document.getElementById('artist-albums-section');
|
||||||
hero.style.background = 'linear-gradient(135deg, var(--surface2), var(--surface))';
|
hero.style.background = 'linear-gradient(135deg, var(--surface2), var(--surface))';
|
||||||
hero.querySelector('.artist-hero-img')?.remove();
|
hero.querySelector('.artist-hero-img')?.remove();
|
||||||
|
hero.querySelector('.artist-hero-tint')?.remove();
|
||||||
const heroBgEl = document.getElementById('artist-hero-bg');
|
const heroBgEl = document.getElementById('artist-hero-bg');
|
||||||
if(heroBgEl) heroBgEl.style.backgroundImage = '';
|
if(heroBgEl) heroBgEl.style.backgroundImage = '';
|
||||||
document.getElementById('artist-hero-pfp-wrap')?.querySelector('.artist-hero-pfp')?.remove();
|
document.getElementById('artist-hero-pfp-wrap')?.querySelector('.artist-hero-pfp')?.remove();
|
||||||
const pfpInitial = document.getElementById('artist-hero-pfp-initial');
|
const pfpInitial = document.getElementById('artist-hero-pfp-initial');
|
||||||
if(pfpInitial) pfpInitial.textContent = artist.name?.[0]?.toUpperCase() || '?';
|
if(pfpInitial) { pfpInitial.textContent = artist.name?.[0]?.toUpperCase() || '?'; pfpInitial.style.display = ''; }
|
||||||
const overlay = hero.querySelector('.artist-hero-overlay');
|
const overlay = hero.querySelector('.artist-hero-overlay');
|
||||||
[bioEl, simEl, tsEl, albEl].forEach(el => { if(el) el.innerHTML = ''; });
|
[bioEl, simEl, tsEl, albEl].forEach(el => { if(el) el.innerHTML = ''; });
|
||||||
if(albEl) albEl.innerHTML = '<div class="spinner"></div>';
|
if(albEl) albEl.innerHTML = '<div class="spinner"></div>';
|
||||||
@@ -114,10 +115,12 @@ export async function loadArtistDetail(artist) {
|
|||||||
const img = imgRes.status === 'fulfilled' ? imgRes.value : null;
|
const img = imgRes.status === 'fulfilled' ? imgRes.value : null;
|
||||||
const _firstAlbums = albumRes.status === 'fulfilled' ? (albumRes.value?.artist?.album || []) : [];
|
const _firstAlbums = albumRes.status === 'fulfilled' ? (albumRes.value?.artist?.album || []) : [];
|
||||||
const _bgCover = _firstAlbums[0] ? coverUrl(_firstAlbums[0].coverArt, 512) : null;
|
const _bgCover = _firstAlbums[0] ? coverUrl(_firstAlbums[0].coverArt, 512) : null;
|
||||||
if(_bgCover || img) {
|
// Option 1: artist photo as background (falls back to album art)
|
||||||
|
const bgSrc = img || _bgCover;
|
||||||
|
if(bgSrc) {
|
||||||
hero.style.background = '';
|
hero.style.background = '';
|
||||||
const bgEl = document.getElementById('artist-hero-bg');
|
const bgEl = document.getElementById('artist-hero-bg');
|
||||||
if(bgEl) bgEl.style.backgroundImage = `url('${_bgCover || img}')`;
|
if(bgEl) bgEl.style.backgroundImage = `url('${bgSrc}')`;
|
||||||
}
|
}
|
||||||
if(img) {
|
if(img) {
|
||||||
const pfpWrap = document.getElementById('artist-hero-pfp-wrap');
|
const pfpWrap = document.getElementById('artist-hero-pfp-wrap');
|
||||||
@@ -130,6 +133,15 @@ export async function loadArtistDetail(artist) {
|
|||||||
const initial = document.getElementById('artist-hero-pfp-initial');
|
const initial = document.getElementById('artist-hero-pfp-initial');
|
||||||
if(initial) initial.style.display = 'none';
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Bio ──
|
// ── Bio ──
|
||||||
@@ -393,3 +405,27 @@ export function expandArtistBio() {
|
|||||||
const el = document.getElementById('artist-bio-text');
|
const el = document.getElementById('artist-bio-text');
|
||||||
if(el && window._artistBioFull) el.textContent = window._artistBioFull;
|
if(el && window._artistBioFull) el.textContent = window._artistBioFull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function _extractColor(src) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const img = new Image();
|
||||||
|
img.crossOrigin = 'anonymous';
|
||||||
|
img.onload = () => {
|
||||||
|
try {
|
||||||
|
const c = document.createElement('canvas');
|
||||||
|
c.width = c.height = 64;
|
||||||
|
const ctx = c.getContext('2d');
|
||||||
|
ctx.drawImage(img, 0, 0, 64, 64);
|
||||||
|
const d = ctx.getImageData(0, 0, 64, 64).data;
|
||||||
|
let r = 0, g = 0, b = 0, n = 0;
|
||||||
|
for(let i = 0; i < d.length; i += 4) {
|
||||||
|
const lum = d[i] * 0.299 + d[i+1] * 0.587 + d[i+2] * 0.114;
|
||||||
|
if(lum > 20 && lum < 235) { r += d[i]; g += d[i+1]; b += d[i+2]; n++; }
|
||||||
|
}
|
||||||
|
resolve(n ? [Math.round(r/n), Math.round(g/n), Math.round(b/n)] : null);
|
||||||
|
} catch { resolve(null); }
|
||||||
|
};
|
||||||
|
img.onerror = () => resolve(null);
|
||||||
|
img.src = src;
|
||||||
|
});
|
||||||
|
}
|
||||||
+1
-1
@@ -822,7 +822,7 @@ button:focus{outline:none}
|
|||||||
.artist-action-btn{display:flex;align-items:center;gap:6px;padding:9px 18px;border-radius:20px;border:1px solid var(--border2);background:var(--surface);color:var(--text);font-family:var(--font);font-size:13px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s}
|
.artist-action-btn{display:flex;align-items:center;gap:6px;padding:9px 18px;border-radius:20px;border:1px solid var(--border2);background:var(--surface);color:var(--text);font-family:var(--font);font-size:13px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s}
|
||||||
.artist-action-btn:hover{background:var(--surface2);border-color:var(--accent);color:var(--accent)}
|
.artist-action-btn:hover{background:var(--surface2);border-color:var(--accent);color:var(--accent)}
|
||||||
.artist-hero{height:240px;border-radius:14px;overflow:hidden;position:relative;margin-bottom:24px;background:linear-gradient(135deg,var(--surface2),var(--surface))}
|
.artist-hero{height:240px;border-radius:14px;overflow:hidden;position:relative;margin-bottom:24px;background:linear-gradient(135deg,var(--surface2),var(--surface))}
|
||||||
.artist-hero-bg{position:absolute;inset:0;background-size:cover;background-position:center;filter:blur(18px) brightness(0.62) saturate(0.65);transform:scale(1.1)}
|
.artist-hero-bg{position:absolute;inset:0;background-size:cover;background-position:center;filter:blur(20px) brightness(0.55) saturate(0.7);transform:scale(1.1)}
|
||||||
.artist-hero-overlay{position:absolute;inset:0;display:flex;align-items:stretch;gap:0}
|
.artist-hero-overlay{position:absolute;inset:0;display:flex;align-items:stretch;gap:0}
|
||||||
.artist-hero-pfp-wrap{aspect-ratio:1/1;height:100%;flex-shrink:0;overflow:hidden;position:relative;background:linear-gradient(135deg,var(--surface2),var(--surface3,var(--surface)))}
|
.artist-hero-pfp-wrap{aspect-ratio:1/1;height:100%;flex-shrink:0;overflow:hidden;position:relative;background:linear-gradient(135deg,var(--surface2),var(--surface3,var(--surface)))}
|
||||||
.artist-hero-pfp{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
|
.artist-hero-pfp{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
|
||||||
|
|||||||
Reference in new issue
Block a user