UNPKG

luminomorphism

Version:

A UI design system built around light, blur, ambient motion and perceptual feedback.

113 lines (100 loc) 7.04 kB
class f extends HTMLElement{static get observedAttributes(){return["value","max","state","nodes","layers","speed","color"]}constructor(){super(),this.attachShadow({mode:"open"}),this.config={value:0,max:100,state:"loading",nodes:6,layers:3,speed:1,color:"#00ffff"},this.neural={network:[],connections:[],pulses:[]},this.animationId=null,this.frameCount=0}connectedCallback(){this.parseAttributes(),this.render(),setTimeout(()=>{this.initNetwork(),this.startAnimation()},100)}disconnectedCallback(){this.animationId&&cancelAnimationFrame(this.animationId)}attributeChangedCallback(t,s,o){s!==o&&(this.parseAttributes(),t==="value"&&this.updateProgress())}parseAttributes(){this.config.value=Math.max(0,Math.min(parseFloat(this.getAttribute("value"))||0,100)),this.config.max=parseFloat(this.getAttribute("max"))||100,this.config.state=this.getAttribute("state")||"loading",this.config.nodes=parseInt(this.getAttribute("nodes"))||6,this.config.layers=Math.max(2,parseInt(this.getAttribute("layers"))||3),this.config.speed=parseFloat(this.getAttribute("speed"))||1,this.config.color=this.getAttribute("color")||this.getStateColor()}getStateColor(){return{loading:"#00ffff",success:"#00ff80",error:"#ff4444",idle:"#666666"}[this.config.state]||"#00ffff"}render(){const t=this.config.color||this.getStateColor();this.shadowRoot.innerHTML=` <style> :host { display: block; width: 100%; height: 120px; position: relative; } .container { width: 100%; height: 100%; background: linear-gradient(90deg, rgba(255,255,255,0.02) 0%, rgba(255,255,255,0.01) 50%, rgba(255,255,255,0.02) 100%); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; position: relative; overflow: hidden; } .info { position: absolute; top: 10px; left: 15px; font-size: 0.8rem; color: ${t}; font-weight: 600; z-index: 10; } .state { position: absolute; top: 10px; right: 15px; font-size: 0.7rem; color: ${t}; text-transform: uppercase; padding: 4px 8px; background: ${t}20; border: 1px solid ${t}40; border-radius: 12px; z-index: 10; } .network { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .node { position: absolute; width: 10px; height: 10px; border-radius: 50%; background: ${t}40; border: 1px solid ${t}60; transition: all 0.3s ease; } .node.active { background: ${t}; border-color: ${t}; box-shadow: 0 0 15px ${t}80; animation: pulse 2s infinite; } .connection { position: absolute; height: 1px; background: ${t}30; transform-origin: left center; } .connection.active { background: ${t}80; height: 2px; box-shadow: 0 0 5px ${t}60; } .pulse-dot { position: absolute; width: 4px; height: 4px; background: ${t}; border-radius: 50%; box-shadow: 0 0 8px ${t}; animation: travel linear; } @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } } @keyframes travel { 0% { opacity: 1; } 100% { opacity: 0; } } </style> <div class="container"> <div class="info">${Math.round(this.config.value)}%</div> <div class="state">${this.config.state}</div> <div class="network" id="network"></div> </div> `}initNetwork(){const t=this.shadowRoot.getElementById("network");if(!t)return;const s=t.offsetWidth-40,o=t.offsetHeight-40;if(s<=0||o<=0){setTimeout(()=>this.initNetwork(),100);return}this.neural.network=[],t.innerHTML="";for(let e=0;e<this.config.layers;e++){const h=[],r=e===0||e===this.config.layers-1?Math.max(1,Math.floor(this.config.nodes*.7)):this.config.nodes,a=20+e*s/(this.config.layers-1);for(let i=0;i<r;i++){const l=20+o/(r+1)*(i+1),n=document.createElement("div");n.className="node",n.style.left=`${a-5}px`,n.style.top=`${l-5}px`,t.appendChild(n);const d={id:`${e}-${i}`,layer:e,index:i,x:a,y:l,element:n,active:!1};h.push(d)}this.neural.network.push(h)}this.neural.connections=[];for(let e=0;e<this.config.layers-1;e++){const h=this.neural.network[e],r=this.neural.network[e+1];h.forEach(a=>{r.forEach(i=>{if(Math.random()>.2){const l=i.x-a.x,n=i.y-a.y,d=Math.sqrt(l*l+n*n),u=Math.atan2(n,l)*(180/Math.PI),c=document.createElement("div");c.className="connection",c.style.left=`${a.x}px`,c.style.top=`${a.y}px`,c.style.width=`${d}px`,c.style.transform=`rotate(${u}deg)`,t.appendChild(c),this.neural.connections.push({from:a,to:i,element:c,active:!1})}})})}this.updateProgress()}updateProgress(){const t=this.config.value/this.config.max;this.neural.network.forEach((o,e)=>{const h=Math.max(0,t*this.neural.network.length-e);o.forEach((r,a)=>{const i=a<Math.floor(h*o.length);i!==r.active&&(r.active=i,i?r.element.classList.add("active"):r.element.classList.remove("active"))})}),this.neural.connections.forEach(o=>{const e=o.from.active&&o.to.active;e!==o.active&&(o.active=e,e?o.element.classList.add("active"):o.element.classList.remove("active"))});const s=this.shadowRoot.querySelector(".info");s&&(s.textContent=`${Math.round(this.config.value)}%`),this.dispatchEvent(new CustomEvent("progress-change",{detail:{value:this.config.value,percentage:this.config.value/this.config.max*100}}))}createPulse(){const t=this.neural.connections.filter(n=>n.active);if(t.length===0)return;const s=t[Math.floor(Math.random()*t.length)],o=this.shadowRoot.getElementById("network"),e=document.createElement("div");e.className="pulse-dot",e.style.left=`${s.from.x-2}px`,e.style.top=`${s.from.y-2}px`,e.style.animationDuration=`${1/this.config.speed}s`,o.appendChild(e);const h=s.to.x-s.from.x,r=s.to.y-s.from.y,a=Date.now(),i=1e3/this.config.speed,l=()=>{const n=Date.now()-a,d=Math.min(n/i,1),u=s.from.x+h*d-2,c=s.from.y+r*d-2;e.style.left=`${u}px`,e.style.top=`${c}px`,d<1?requestAnimationFrame(l):e.remove()};l()}startAnimation(){const t=()=>{this.frameCount++,this.frameCount%30===0&&Math.random()<.3&&this.createPulse(),this.animationId=requestAnimationFrame(t)};t()}setValue(t){this.setAttribute("value",t)}setState(t){this.setAttribute("state",t),this.render(),setTimeout(()=>this.initNetwork(),50)}pulse(){for(let t=0;t<5;t++)setTimeout(()=>this.createPulse(),t*200)}reset(){this.setValue(0)}}customElements.define("l-neural-progress",f); //# sourceMappingURL=l-neural-progress.min.js.map