<html> <head> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css"> <style> body { margin: 0; padding: 0; } #scroll-trigger-container { position: relative; height: 500vh; width: 100%; } .pinned-container { position: sticky; top: 0; height: 0; } .ball { position: absolute; width: 5rem; height: 5rem; border-radius: 100%; background-color: red; } .line { position: absolute; width: 0.2rem; background: black; border-radius: 100%; opacity: 0; } .ball-1 { top: calc(20% - 0.5 * 5rem); left: calc(43% - 0.5 * 5rem); } .ball-2 { top: calc(35% - 0.5 * 5rem); left: calc(70% - 0.5 * 5rem); } .ball-3 { top: calc(45% - 0.5 * 5rem); left: calc(32% - 0.5 * 5rem); } .ball-4 { top: calc(55% - 0.5 * 5rem); left: calc(60% - 0.5 * 5rem); } .ball-5 { top: calc(65% - 0.5 * 5rem); left: calc(50% - 0.5 * 5rem); } .ball-6 { top: calc(75% - 0.5 * 5rem); left: calc(55% - 0.5 * 5rem); } .line-1 { left: calc(50% - 0.1rem); height: 80%; } .ball-line { opacity: 0; position: absolute; width: 0.2rem; background: black; border-radius: 100%; } .ball-line-1 { top: calc(50% - 0.5 * 0.2rem); width: 500%; height: 0.2rem; left: -10rem; } .ball-line-2 { top: calc(50% - 0.5 * 0.2rem); width: 500%; height: 0.2rem; left: -10rem; } .ball-line-3 { top: calc(50% - 0.5 * 0.2rem); width: 500%; height: 0.2rem; left: -10rem; } .ball-line-4 { top: calc(50% - 0.5 * 0.2rem); width: 500%; height: 0.2rem; left: -10rem; } .ball-line-5 { top: calc(50% - 0.5 * 0.2rem); width: 500%; height: 0.2rem; left: -10rem; } .ball-line-6 { top: calc(50% - 0.5 * 0.2rem); width: 500%; height: 0.2rem; left: -10rem; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.7.0/gpx.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/ScrollTrigger.min.js"></script> <script src="https://hammerjs.github.io/dist/hammer.min.js"></script> <div id="scroll-trigger-container"> <div class="pinned-container"> <div class="ball ball-1"><div class="ball-line ball-line-1"></div></div> <div class="ball ball-2"><div class="ball-line ball-line-2"></div></div> <div class="ball ball-3"><div class="ball-line ball-line-3"></div></div> <div class="ball ball-4"><div class="ball-line ball-line-4"></div></div> <div class="ball ball-5"><div class="ball-line ball-line-5"></div></div> <div class="ball ball-6"><div class="ball-line ball-line-6"></div></div> <div class="line line-1"></div> <div class="line"></div> <div class="line"></div> <div class="line"></div> <div class="line"></div> </div> </div> </body> </html> <script> gsap.registerPlugin(ScrollTrigger); const progressRef = document.querySelector('progress'); const eventListener = (e) => { gsap.to('.ball-1', {x: e.clientX, y: e.clientY, top: '0', left: '0', duration: 1}); gsap.to('.ball-line-1', {opacity: 0}); } const pinnedContainerRef = document.querySelector('.pinned-container'); const timeline = gsap.timeline({ scrollTrigger: { trigger: "#scroll-trigger-container", markers: true, scrub: true, start: "top top", end: "bottom bottom", onLeave: (e) => { pinnedContainerRef.addEventListener('mousemove', eventListener); }, onEnterBack: () => { pinnedContainerRef.removeEventListener('mousemove', eventListener); gsap.to('.ball-1', {left: 'calc(43% - 0.5 * 5rem)', top: 'calc(20% - 0.5 * 5rem)', x: '0', y: '0', duration: 1}); gsap.to('.ball-line-1', {opacity: 0}); } } }); timeline.addLabel('spread-circles') .to('.pinned-container', {height: '100vh'}) .to('.line', {opacity: 1}) .to('.ball-line', {opacity: 1}) </script>