62 lines
5.8 KiB
JavaScript
62 lines
5.8 KiB
JavaScript
(() => {
|
||
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);
|
||
})();
|