testing

Moss Frame Interactive
function resize(){ const dpr=devicePixelRatio||1; canvas.width=innerWidth*dpr; canvas.height=innerHeight*dpr; canvas.style.width=innerWidth+"px"; canvas.style.height=innerHeight+"px"; ctx.setTransform(dpr,0,0,dpr,0,0); drawFrame(currentFrame); } window.addEventListener("resize",resize); frameNames.forEach((src,i)=>{ let img=new Image(); img.src=src; img.onload=()=>{ images[i]=img; loaded++; if(loaded===frameNames.length){ resize(); drawFrame(0); startAnimation(); } } }); function drawFrame(index){ if(!images[index])return; currentFrame=index; let img=images[index]; let w=canvas.width/(devicePixelRatio||1); let h=canvas.height/(devicePixelRatio||1); ctx.clearRect(0,0,w,h); let scale=Math.min( w/img.width, h/img.height ); let width=img.width*scale; let height=img.height*scale; ctx.drawImage( img, (w-width)/2, (h-height)/2, width, height ); } function toggleHotspots(show){ hotspots.forEach(h=>{ gsap.to(h,{ opacity:show?1:0, scale:show?1:0, duration:.3 }); }); } function startAnimation(){ gsap.to(sequence,{ frame:lastFrame, snap:"frame", ease:"none", scrollTrigger:{ trigger:".sequence-section", start:"top top", end:"bottom bottom", scrub:.5, onUpdate:()=>{ let frame=Math.round(sequence.frame); drawFrame(frame); // ONLY SHOW ON LAST IMAGE if(frame===lastFrame){ toggleHotspots(true); } else{ toggleHotspots(false); infoCard.classList.remove("show"); } } } }); } hotspots.forEach(h=>{ h.addEventListener("mouseenter",()=>{ if(currentFrame!==lastFrame)return; let data=hotspotInfo[h.id]; cardImage.src=data.image; cardTitle.textContent=data.title; cardDescription.textContent=data.description; let rect=h.getBoundingClientRect(); infoCard.style.left=(rect.left+30)+"px"; infoCard.style.top=(rect.top-30)+"px"; infoCard.classList.add("show"); }); h.addEventListener("mouseleave",()=>{ infoCard.classList.remove("show"); }); }); ScrollTrigger.refresh();