vdi-cloud/ui/terminal/assets/login-art.js

62 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(() => {
const host=document.getElementById('ill');
host.querySelectorAll('.px,.work-art,#sky').forEach(e=>e.remove());
const canvas=document.createElement('canvas');canvas.className='kinetic-art';
canvas.setAttribute('role','button');canvas.tabIndex=0;
canvas.setAttribute('aria-label','互动树形 Logo:移动鼠标探索,点击或按回车释放光环');
host.prepend(canvas);
const W=780,H=1060,dpr=Math.min(devicePixelRatio||1,2),ctx=canvas.getContext('2d');
canvas.width=W*dpr;canvas.height=H*dpr;ctx.scale(dpr,dpr);
const dark=()=>document.body.dataset.theme==='dark';
const reduced=matchMedia('(prefers-reduced-motion: reduce)');
const mark=new Image();mark.src='assets/mark.svg';
const plate=document.createElement('canvas');plate.width=1000;plate.height=850;
const paint=plate.getContext('2d');
const grain=document.createElement('canvas');grain.width=1000;grain.height=850;
const grainPaint=grain.getContext('2d');let seed=37;
const random=()=>{seed=(seed*16807)%2147483647;return seed/2147483647;};
for(let i=0;i<2200;i++){const x=random()*1000,y=random()*850;grainPaint.strokeStyle=i%3?'rgba(255,255,255,.12)':'rgba(153,111,54,.06)';grainPaint.lineWidth=.5;grainPaint.beginPath();grainPaint.moveTo(x,y);grainPaint.lineTo(x+8+random()*45,y-1);grainPaint.stroke();}
const p={x:0,y:0,tx:0,ty:0,mx:-1000,my:-1000,inside:false};
let ready=false,frame=0,clicks=0,last=0,raf=0;
const rings=[];
const leaves=[[.10,.46],[.25,.26],[.50,.13],[.75,.26],[.90,.46]];
function locate(e){const r=host.getBoundingClientRect();p.mx=(e.clientX-r.left)*W/r.width;p.my=(e.clientY-r.top)*H/r.height;p.tx=(p.mx/W-.5)*2;p.ty=(p.my/H-.5)*2;p.inside=true;}
host.addEventListener('pointermove',locate);
host.addEventListener('pointerleave',()=>{p.inside=false;p.tx=p.ty=0;p.mx=p.my=-1000;});
function pulse(){clicks++;canvas.dataset.clicks=clicks;if(!reduced.matches)rings.push({start:performance.now()});}
canvas.addEventListener('pointerdown',e=>{locate(e);pulse();});
canvas.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();pulse();}});
mark.onload=()=>{ready=true;canvas.dataset.ready='true';};
function draw(now){
const dt=Math.min(40,now-last||16);last=now;frame++;
const smooth=1-Math.exp(-dt/140);p.x+=(p.tx-p.x)*smooth;p.y+=(p.ty-p.y)*smooth;
ctx.clearRect(0,0,W,H);
const bg=ctx.createLinearGradient(0,0,W,H);bg.addColorStop(0,dark()?'#1B222B':'#FCF8F5');bg.addColorStop(1,dark()?'#262930':'#F5E6DA');ctx.fillStyle=bg;ctx.fillRect(0,0,W,H);
const t=reduced.matches?0:now*.001;
const cx=344+(reduced.matches?0:p.x*22),cy=658+(reduced.matches?0:p.y*15+Math.sin(t*.7)*7);
const w=470,h=398,left=cx-w/2,top=cy-h/2;
const halo=ctx.createRadialGradient(cx,cy,30,cx,cy,345);halo.addColorStop(0,dark()?'rgba(230,161,94,.10)':'rgba(255,255,255,.95)');halo.addColorStop(1,'rgba(230,161,94,0)');ctx.fillStyle=halo;ctx.fillRect(0,330,780,730);
ctx.save();ctx.translate(cx,cy+235);ctx.scale(1,.13);const shadow=ctx.createRadialGradient(0,0,10,0,0,245);shadow.addColorStop(0,dark()?'rgba(0,0,0,.22)':'rgba(151,92,46,.09)');shadow.addColorStop(1,'rgba(151,92,46,0)');ctx.fillStyle=shadow;ctx.fillRect(-260,-260,520,520);ctx.restore();
if(ready){
paint.clearRect(0,0,1000,850);paint.globalCompositeOperation='source-over';paint.drawImage(mark,0,0,1000,850);paint.globalCompositeOperation='source-in';
const lightX=reduced.matches?0:p.x*120+Math.sin(t*.35)*35;
const g=paint.createLinearGradient(-100+lightX,50,950+lightX,760);g.addColorStop(0,'#FFF0CE');g.addColorStop(.23,'#F1CF92');g.addColorStop(.43,'#E3B76E');g.addColorStop(.58,'#F9DFA8');g.addColorStop(.72,'#E9BF7B');g.addColorStop(1,'#F7DCA5');paint.fillStyle=g;paint.fillRect(0,0,1000,850);
paint.globalCompositeOperation='source-atop';
const sheen=paint.createLinearGradient(lightX,0,1000+lightX,500);sheen.addColorStop(0,'rgba(255,255,255,0)');sheen.addColorStop(.42,'rgba(255,255,255,0)');sheen.addColorStop(.53,'rgba(255,252,237,.32)');sheen.addColorStop(.64,'rgba(255,255,255,0)');sheen.addColorStop(1,'rgba(255,255,255,0)');paint.fillStyle=sheen;paint.fillRect(0,0,1000,850);paint.drawImage(grain,0,0);
ctx.globalAlpha=.07;for(let i=4;i>0;i-=1)ctx.drawImage(mark,left+i*.45,top+i*.7,w,h);ctx.globalAlpha=1;
ctx.save();ctx.filter='brightness(1.12)';ctx.drawImage(plate,left-.6,top-.8,w,h);ctx.restore();
ctx.drawImage(plate,left,top,w,h);
leaves.forEach(([x,y],i)=>{const lx=left+x*w,ly=top+y*h,d=Math.hypot(lx-p.mx,ly-p.my),active=p.inside?Math.max(0,1-d/115):0;
if(active>0){ctx.fillStyle=`rgba(255,231,188,${active*.6})`;ctx.shadowColor='#F8CE91';ctx.shadowBlur=22*active;ctx.beginPath();ctx.arc(lx,ly,5+active*8,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;}
});
}
// Sparse orbital sparks react locally to the pointer, leaving the silhouette clear.
for(let i=0;i<38;i++){const a=i*2.399+t*.09,r=255+(i%5)*13;let x=cx+Math.cos(a)*r,y=cy+Math.sin(a)*r*.81;const d=Math.hypot(x-p.mx,y-p.my);if(p.inside&&d<130&&!reduced.matches){x+=(p.mx-x)*.22;y+=(p.my-y)*.22;ctx.strokeStyle='rgba(217,139,70,.2)';ctx.beginPath();ctx.moveTo(x,y);ctx.lineTo(p.mx,p.my);ctx.stroke();}ctx.fillStyle=dark()?'rgba(246,196,133,.55)':'rgba(189,113,59,.42)';ctx.beginPath();ctx.arc(x,y,i%4===0?2:1,0,Math.PI*2);ctx.fill();}
for(let i=rings.length-1;i>=0;i--){const age=(now-rings[i].start)/1500;if(age>=1){rings.splice(i,1);continue;}ctx.strokeStyle=`rgba(230,161,94,${(1-age)*.6})`;ctx.lineWidth=1.5;ctx.beginPath();ctx.ellipse(cx,cy,70+age*300,50+age*235,0,0,Math.PI*2);ctx.stroke();}
canvas.dataset.pointer=`${p.x.toFixed(3)},${p.y.toFixed(3)}`;canvas.dataset.frame=frame;canvas.dataset.rings=rings.length;canvas.dataset.reduced=String(reduced.matches);
raf=requestAnimationFrame(draw);
}
document.addEventListener('visibilitychange',()=>{cancelAnimationFrame(raf);if(!document.hidden){last=0;raf=requestAnimationFrame(draw);}});
raf=requestAnimationFrame(draw);
})();