<h1 class="header-section">Scroll to see a timeline animation</h1>

<svg id="svg-stage" xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 600 1200">
  <path class="line01 line" d="M 10 200 600 200" ></path>
  <path class="line02 line" d="M 10 400 600 400" ></path>
  <path class="line03 line" d="M 10 600 600 600" ></path>
  <path class="line04 line" d="M 10 800 600 800" ></path>
  <path class="line05 line" d="M 10 1000 600 1000" ></path>
  <text class="text01" x="30" y="190">2018</text>
  <text class="text02" x="30" y="390">2019</text>
  <text class="text03" x="30" y="590">2020</text>

  <path class="theLine" 
        d="M -5,0
           Q 450 230 300 450 
           T 130 750
           Q 100 850 300 1000
           T 150 1200"
        fill="none" stroke="white" stroke-width="10px" />
  
  <circle class="ball ball01" r="20" cx="50" cy="100"></circle>
  <circle class="ball ball02" r="20" cx="278" cy="201"></circle>
  <circle class="ball ball03" r="20" cx="327" cy="401"></circle>
  <circle class="ball ball04" r="20" cx="203" cy="601"></circle>

</svg>
@font-face {
  font-display: block;
  font-family: Mori;
  font-style: normal;
  font-weight: 400;
  src: url(https://assets.codepen.io/16327/PPMori-Regular.woff) format("woff");
}

body {
  width:100%;
	height: 400vh;
  background: black;
  color: white;
  font-family: "Mori", sans-serif;
}

.header-section {
  position:relative;
  text-align:center;
  margin: 100px auto 0;
}

#svg-stage {
  max-width: 600px;
  overflow: visible;
  margin-top: 60vh;
}

.ball {
  fill: white;
  visibility: hidden;
}

.line {
  fill: none;
  stroke: white;
  stroke-width: 2px;
}

text {
  fill: white;
  font-size: 15px; 
  visibility: hidden;
}
console.clear();

gsap.registerPlugin(ScrollTrigger, DrawSVGPlugin, MotionPathPlugin);
gsap.defaults({ease: "none"});



const pulses = gsap.timeline({
  defaults: {
    duration: 0.05, 
    autoAlpha: 1, 
    scale: 2, 
    transformOrigin: 'center', 
    ease: "elastic(2.5, 1)"
  }})
.to(".ball02, .text01", {}, 0.2) 
.to(".ball03, .text02", {}, 0.33)
.to(".ball04, .text03", {}, 0.46)

const main = gsap.timeline({defaults: {duration: 1},
  scrollTrigger: {
    trigger: "#svg-stage",
    scrub: true,
    start: "top center",
    end: "bottom center"
  }})
.to(".ball01", {duration: 0.01, autoAlpha: 1})
.from(".theLine", {drawSVG: 0}, 0)
.to(".ball01", {motionPath: {
  path: ".theLine", 
  align:".theLine",
  alignOrigin: [0.5, 0.5],
}}, 0)
.add(pulses, 0);





// 💚 This just adds the GSAP link to this pen, don't copy this bit
import { GSAPInfoBar } from "https://codepen.io/GreenSock/pen/vYqpyLg.js"
new GSAPInfoBar({ link: "https://gsap.com/docs/v3/Plugins/ScrollTrigger/"});
// 💚 Happy tweening!

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/gsap-latest-beta.min.js
  2. https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/ScrollTrigger.min.js
  3. https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/DrawSVGPlugin3.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/MotionPathPlugin.min.js