Files
argo/web/cursor-light.js

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()});
})();