discovery: elevate the wave dial — full width + glow, luminous fill, pulsing orb, colour aura

This commit is contained in:
BoulderBadgeDad 2026-06-29 16:48:34 -07:00
parent c50465984f
commit d9bd5c1d7f
3 changed files with 65 additions and 25 deletions

View file

@ -3156,8 +3156,16 @@
<span class="adv-wave-state" id="adv-wave-state">Balanced</span>
</div>
<div class="adv-wave-track" id="adv-wave-track">
<div class="adv-wave-aura" id="adv-wave-aura"></div>
<svg class="adv-wave-svg" id="adv-wave-svg" viewBox="0 0 1000 80" preserveAspectRatio="none" aria-hidden="true">
<path id="adv-wave-path" fill="none" stroke-width="3" stroke-linecap="round"></path>
<defs>
<linearGradient id="adv-wave-fill" x1="0" y1="0" x2="0" y2="1">
<stop id="adv-wave-fill-top" offset="0" stop-color="#1DB954" stop-opacity="0.32"/>
<stop offset="1" stop-color="#1DB954" stop-opacity="0"/>
</linearGradient>
</defs>
<path id="adv-wave-area" fill="url(#adv-wave-fill)" stroke="none"></path>
<path id="adv-wave-path" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
<div class="adv-wave-orb" id="adv-wave-orb"></div>
</div>

View file

@ -36,9 +36,10 @@ function _advState(v) {
return 'Deep cuts only';
}
// green (120°) → red (0°) through the warm spectrum (yellow, orange) as the orb moves right.
function _advColor(v, light) {
function _advColor(v, light, alpha) {
const hue = 120 * (1 - Math.max(0, Math.min(1, v)));
return `hsl(${hue.toFixed(0)}, 85%, ${light || 55}%)`;
const l = light || 55;
return alpha != null ? `hsla(${hue.toFixed(0)}, 85%, ${l}%, ${alpha})` : `hsl(${hue.toFixed(0)}, 85%, ${l}%)`;
}
// Wave height (viewBox units, centre 40) at position u (0..1) for adventurousness v.
function _advWaveY(u, v) {
@ -57,14 +58,16 @@ function _advDraw() {
if (track.offsetParent !== null) { // skip the work while the Discover page is hidden
const v = _advWave.value;
_advWave.phase += 0.03 + v * 0.07; // waves faster the more adventurous
let d = '';
let line = '';
const N = 90;
for (let i = 0; i <= N; i++) {
const u = i / N;
d += (i === 0 ? 'M ' : ' L ') + (u * 1000).toFixed(1) + ' ' + _advWaveY(u, v).toFixed(1);
line += (i === 0 ? 'M ' : ' L ') + (u * 1000).toFixed(1) + ' ' + _advWaveY(u, v).toFixed(1);
}
path.setAttribute('d', d);
path.setAttribute('stroke', _advColor(v));
path.setAttribute('d', line);
// the luminous filled area = the wave closed down to the baseline
const area = document.getElementById('adv-wave-area');
if (area) area.setAttribute('d', line + ' L 1000 80 L 0 80 Z');
const orb = document.getElementById('adv-wave-orb');
if (orb) orb.style.top = (_advWaveY(v, v) / 80 * 100).toFixed(2) + '%'; // orb rides the wave
}
@ -73,15 +76,27 @@ function _advDraw() {
function _advApply(v) {
v = Math.max(0, Math.min(1, v));
_advWave.value = v;
const c = _advColor(v, 55);
const cBright = _advColor(v, 62);
// Line stroke + its colour glow (set on change, not every frame, so the rAF stays cheap).
const path = document.getElementById('adv-wave-path');
if (path) { path.setAttribute('stroke', c); path.style.filter = `drop-shadow(0 0 7px ${c})`; }
const fillTop = document.getElementById('adv-wave-fill-top');
if (fillTop) fillTop.setAttribute('stop-color', c); // luminous filled area
const orb = document.getElementById('adv-wave-orb');
if (orb) {
orb.style.left = (v * 100).toFixed(2) + '%';
const c = _advColor(v, 58);
orb.style.background = c;
orb.style.boxShadow = `0 0 18px 2px ${c}, 0 0 0 5px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.55)`;
orb.style.color = c; // currentColor for the pulsing ring
orb.style.background = cBright;
orb.style.boxShadow = `0 0 22px 3px ${c}, 0 0 0 6px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.6)`;
}
const aura = document.getElementById('adv-wave-aura'); // colour wash that follows the orb
if (aura) {
aura.style.left = (v * 100).toFixed(2) + '%';
aura.style.background = `radial-gradient(circle, ${_advColor(v, 50, 0.42)} 0%, transparent 68%)`;
}
const stateEl = document.getElementById('adv-wave-state');
if (stateEl) { stateEl.textContent = _advState(v); stateEl.style.color = _advColor(v, 62); }
if (stateEl) { stateEl.textContent = _advState(v); stateEl.style.color = cBright; }
}
let _advCommitTimer = null;
function _advCommit(v) {

View file

@ -35168,35 +35168,52 @@ div.artist-hero-badge {
and wilder + redder at the right (obscure). The path + colours are drawn each frame in
JS (rAF). Syncs with Settings Discovery. */
.adv-wave {
max-width: 880px;
margin: 0 0 26px;
padding: 16px 22px 14px;
border-radius: 16px;
background: linear-gradient(135deg, rgba(255,255,255,0.035), rgba(255,255,255,0.01));
position: relative;
margin: 0 0 28px;
padding: 18px 26px 16px;
border-radius: 18px;
background: linear-gradient(135deg, rgba(255,255,255,0.04), rgba(255,255,255,0.012));
border: 1px solid rgba(255,255,255,0.07);
box-shadow: 0 8px 28px rgba(0,0,0,0.25);
box-shadow: 0 10px 34px rgba(0,0,0,0.30);
overflow: hidden; /* contain the colour aura wash */
}
.adv-wave-head { display: flex; align-items: baseline; gap: 12px; }
.adv-wave-title { font-size: 14px; font-weight: 700; color: #fff; }
.adv-wave-head { display: flex; align-items: baseline; gap: 12px; position: relative; z-index: 2; }
.adv-wave-title { font-size: 15px; font-weight: 800; color: #fff; letter-spacing: 0.2px; }
.adv-wave-state { font-size: 12px; font-weight: 700; transition: color 0.25s; }
.adv-wave-track {
position: relative; height: 84px; margin: 4px 0 2px;
position: relative; height: 104px; margin: 6px 0 2px;
cursor: grab; touch-action: none;
}
.adv-wave-track:active { cursor: grabbing; }
.adv-wave-svg { width: 100%; height: 100%; display: block; overflow: visible; }
.adv-wave-orb {
.adv-wave-aura {
position: absolute; top: 50%; left: 30%;
width: 20px; height: 20px; border-radius: 50%;
width: 360px; height: 230px; transform: translate(-50%, -50%);
pointer-events: none; filter: blur(14px); z-index: 0;
will-change: left, background;
}
.adv-wave-svg { width: 100%; height: 100%; display: block; overflow: visible; position: relative; z-index: 1; }
.adv-wave-orb {
position: absolute; top: 50%; left: 30%; z-index: 2;
width: 22px; height: 22px; border-radius: 50%;
transform: translate(-50%, -50%);
background: #1DB954;
box-shadow: 0 0 18px 2px #1DB954, 0 0 0 5px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.55);
background: #1DB954; color: #1DB954;
box-shadow: 0 0 20px 3px #1DB954, 0 0 0 6px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.6);
pointer-events: none; /* the track captures the pointer; the orb is purely visual */
will-change: left, top;
}
.adv-wave-orb::after {
content: ''; position: absolute; inset: -7px; border-radius: 50%;
border: 2px solid currentColor; opacity: 0.5;
animation: adv-orb-pulse 2.4s ease-out infinite;
}
@keyframes adv-orb-pulse {
0% { transform: scale(0.7); opacity: 0.55; }
100% { transform: scale(1.95); opacity: 0; }
}
.adv-wave-ends {
display: flex; justify-content: space-between;
font-size: 11px; font-weight: 500; color: rgba(255,255,255,0.35);
position: relative; z-index: 2;
}
/* Watchlist toggle on recommended-artist .ya-cards corner pill, appears on hover,