luminomorphism
Version:
A UI design system built around light, blur, ambient motion and perceptual feedback.
20 lines (18 loc) • 1.99 kB
JavaScript
class LRippleHover extends HTMLElement{constructor(){super(),this.color=this.getAttribute("color")||"#00ffff",this.duration=parseInt(this.getAttribute("duration"))||600,this.selector=this.getAttribute("target")||".l-button",this.activeElements=new Set}static get observedAttributes(){return["color","duration","target"]}attributeChangedCallback(t,i,e){i!==e&&(t==="color"&&(this.color=e),t==="duration"&&(this.duration=parseInt(e)),t==="target"&&(this.selector=e),this.cleanup(),this.attachRippleToTargets())}connectedCallback(){const t=document.createElement("style");t.textContent=`
.ripple-hover {
position: absolute;
border-radius: 50%;
transform: scale(0);
opacity: 0.4;
pointer-events: none;
filter: blur(8px);
animation: ripple-spread ease-out;
z-index: 2;
}
ripple-spread {
to {
transform: scale(1);
opacity: 0;
}
}
`,document.head.appendChild(t),this.attachRippleToTargets()}attachRippleToTargets(){document.querySelectorAll(this.selector).forEach(t=>{if(!this.activeElements.has(t)){t.style.position=t.style.position||"relative",t.style.overflow="hidden";const i=e=>this.createRipple(e,t);t.addEventListener("mouseenter",i),this.activeElements.add(t),t._rippleHandler=i}})}cleanup(){this.activeElements.forEach(t=>{t.removeEventListener("mouseenter",t._rippleHandler),delete t._rippleHandler}),this.activeElements.clear()}createRipple(t,i){const e=document.createElement("span");e.className="ripple-hover";const s=i.getBoundingClientRect(),r=Math.max(s.width,s.height),o=t.clientX-s.left,a=t.clientY-s.top;e.style.left=`${o}px`,e.style.top=`${a}px`,e.style.width=e.style.height=`${r*2}px`,e.style.marginLeft=`-${r}px`,e.style.marginTop=`-${r}px`,e.style.background=this.color,e.style.animationDuration=`${this.duration}ms`,i.appendChild(e),setTimeout(()=>e.remove(),this.duration)}}customElements.define("l-ripple-hover",LRippleHover);