43 lines
2.5 KiB
JavaScript
43 lines
2.5 KiB
JavaScript
/* Decorative only: no control demands or vehicle state are accessed here. */
|
|
(() => {
|
|
const workspace=document.querySelector('.controls-workspace');
|
|
if(!workspace)return;
|
|
const wash=document.createElement('span');
|
|
wash.className='workspace-light';wash.setAttribute('aria-hidden','true');workspace.append(wash);
|
|
// Explicit visual surfaces, not every semantic button. The entire sidebar
|
|
// and text-only Engage control are excluded from local lighting.
|
|
const pillSelector='.config-btn,.modal .button,.range-pill,.number-pill';
|
|
const shapeSelector=`${pillSelector},.heave-track,.heave-knob,.joystick,.modal`;
|
|
const layers=[...document.querySelectorAll(shapeSelector)].filter(host=>!host.closest('.sidebar-deck,#engage')).map(host=>{
|
|
host.classList.add('light-surface');
|
|
const layer=document.createElement('span');layer.className='cursor-light';
|
|
layer.setAttribute('aria-hidden','true');
|
|
layer.classList.toggle('pill-light',host.matches(pillSelector));
|
|
host.append(layer);return {host,layer};
|
|
});
|
|
let frame=0,point=null;
|
|
const clear=()=>{point=null;cancelAnimationFrame(frame);frame=0;wash.classList.remove('lit');layers.forEach(({layer})=>layer.classList.remove('lit','near'))};
|
|
function paint(){
|
|
frame=0;if(!point)return;
|
|
const {x,y}=point,r=workspace.getBoundingClientRect();
|
|
wash.style.setProperty('--light-x',`${x-r.left}px`);wash.style.setProperty('--light-y',`${y-r.top}px`);
|
|
wash.classList.toggle('lit',x>=r.left&&x<=r.right&&y>=r.top&&y<=r.bottom);
|
|
for(const {host,layer} of layers){
|
|
const b=host.getBoundingClientRect();
|
|
layer.style.setProperty('--light-x',`${x-b.left}px`);layer.style.setProperty('--light-y',`${y-b.top}px`);
|
|
const inside=x>=b.left&&x<=b.right&&y>=b.top&&y<=b.bottom;
|
|
// CSS gradient angles start at up; atan2 is measured from the x axis.
|
|
const angle=Math.atan2(y-(b.top+b.height/2),x-(b.left+b.width/2))*180/Math.PI+90;
|
|
layer.style.setProperty('--light-angle',`${angle}deg`);
|
|
layer.classList.toggle('lit',inside);layer.classList.add('near');
|
|
}
|
|
}
|
|
document.addEventListener('pointermove',event=>{
|
|
if(event.pointerType==='touch'){clear();return}
|
|
point={x:event.clientX,y:event.clientY};if(!frame)frame=requestAnimationFrame(paint);
|
|
},{passive:true});
|
|
document.documentElement.addEventListener('pointerleave',clear);
|
|
document.addEventListener('pointercancel',clear);window.addEventListener('blur',clear);
|
|
document.addEventListener('visibilitychange',()=>{if(document.hidden)clear()});
|
|
})();
|