JSFiddle - React, Tailwind, and code Playground
by Michael Mandarino
HTML
<script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script>
<script src="https://unpkg.com/gsap@3/dist/MotionPathPlugin.min.js"></script>
<script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js"></script>
<div id="scrollDist"></div>
<div id="container">
<style type="text/css">
.st0 {
fill: #003878;
}
.st1 {
fill: #FFFFFF;
}
.st2 {
fill: none;
stroke: #000000;
stroke-width: 3;
stroke-miterlimit: 10;
}
.st3 {
fill: #D60909;
}
</style>
<svg id="map" width="1600" height="2000">
<path id="route" class="st2" d="M281,164.7C166.8,420.6,1457.9,140,975.7,309.8S200.6,447,237.2,776.8
C274.6,1113.3,1389,56.5,1473.6,568.2" />
<path id="point" class="st3" d="M821.767 105.048c0-5.546-4.503-10.05-10.05-10.05h-24.531c-5.547 0-10.05 4.504-10.05 10.05l20.367 30.516c5.547 0 24.264-30.516 24.264-30.516z" />
</svg>
</div>
CSS
html,
body {
width: 100%;
height: 100vh;
}
#map {
position: absolute;
overflow: visible;
}
JavaScript
// special definition of 'duration'
// of the scrubbed ScrollTrigger animation
// The higher the value, the slower the animation
var scrollDist = 25;
gsap.set("#scrollDist", {
width: "100%",
height: () => window.innerHeight * (scrollDist + 3)
});
// ==========
var map = document.getElementById("map"),
mapWidth = map.getBoundingClientRect().width,
mapHeight = map.getBoundingClientRect().height;
gsap.set("#container", {
position: "fixed",
width: mapWidth,
height: mapHeight,
left: "50%",
xPercent: -50,
top: 0,
autoAlpha: 1
});
//tween the route + point
gsap
.timeline({
defaults: {
duration: 1,
ease: "none"
},
scrollTrigger: {
trigger: "#scrollDist",
start: "top top",
end: () => window.innerHeight * scrollDist,
scrub: 0.3,
}
})
.to(
"#point", {
motionPath: {
path: "#route",
align: "#route"
}
},
0
);
//move the container so that the moving 'point'
//is always in the same place
let povDelay = 1, // 1 = no delay
pos = {
x: -mapWidth / 2,
y: -mapHeight / 2
},
xSet = gsap.quickSetter("#container", "x", "px"),
ySet = gsap.quickSetter("#container", "y", "px");
gsap.ticker.add(() => {
pos.x += (-gsap.getProperty("#point", "x") - pos.x) * 1;
pos.y += (-gsap.getProperty("#point", "y") - pos.y) * 1;
xSet(pos.x);
ySet(pos.y);
});
// = onresize ===========
window.onresize = () => {
gsap.set("#scrollDist", {
width: "100%",
height: () => window.innerHeight * (scrollDist + 3)
});
};