<div class="container">
  <div class="jumps">Jump to: <a href="#end" id="link">bottom</a> | <a href="#top">top</a></div>
  
	<section class="panel pin">
		<div class="standard">
			<h2>Standard behavior</h2>
			<div class="boxes">
				<div class="box box-1 blue">1</div>
				<div class="box box-2 red">2</div>
				<div class="box box-3 green">3</div>
				<div class="box box-4 purple">4</div>
				<div class="box box-5">5</div>
			</div>
		</div>
		<div class="features">
			<div class="boxes">
				<div class="box box-1 blue">1</div>
				<div class="box box-2 red">2</div>
				<div class="box box-3 green">3</div>
				<div class="box box-4 purple">4</div>
				<div class="box box-5">5</div>
			</div>
			<h2>With features:</h2>
			<div style="display: inline-block">
				<div id="checkboxes">
					<label><input id="checkboxPO" type="checkbox" value="preventOverlaps" checked/>&nbsp;<code>preventOverlaps</code></label>
					<label><input id="checkboxFSE" type="checkbox" value="fastScrollEnd" checked/>&nbsp;<code>fastScrollEnd</code></label>
				</div>
			</div>
		</div>
	</section>
	<section class="panel move-1 gray"></section>
	<section class="panel move-2 blue"></section>
	<section class="panel move-3 red"></section>
	<section class="panel move-4 green"></section>
	<section class="panel move-5 purple"></section>
	<section class="panel spacer gray"></section>
  <a name="end"></a>
</div>
<div class="explanation-container">
<div class="explanation">
	<div class="tab purple">Explanation</div>
	<div class="content">
	<section class="panel purple">
		<div>
      <p>Scroll down <strong>slowly</strong> to trigger <i>non-scrubbing</i> animations. Looks great, right? Then try scrolling <i>fast</i> and notice how the animations overlap! </p>
			<ul>
				<li><code>preventOverlaps: true</code> kicks in when the ScrollTrigger is about to affect its animation (e.g. a toggleAction) - it finds other [prior] ScrollTrigger-based animations and forces them to their end state to avoid overlaps.</li>
				<li> <code>fastScrollEnd: true</code> kicks in only when you <strong>LEAVE</strong> the ScrollTrigger's area; it simply says <i>"how fast was the scroll onLeave/onLeaveBack? If it's above the 2500px/second, force the animation to its end immediately"</i>.</li>
			</ul>
		</div>
	</section>
	<section class="panel gray">
		<div>
			<h2>Advanced configuration</h2>
			<ul>
        <li>If <code>preventOverlaps</code> is a <strong>string</strong>, it acts as a group name so that you can control which ScrollTriggers affect each other's preventOverlaps behavior. It's like setting <code>preventOverlaps: true</code> but only for other ScrollTriggers with the matching string. For example, you could assign <code>preventOverlaps: "group1"</code> (or any arbitrary string) to 3 of your ScrollTriggers so that each of them only prevents overlaps from the other ScrollTriggers that have that same "group1" value.
				</li>
        
				<li>If <code>preventOverlaps</code> is a <strong>function</strong>, it'll be called <i>before</i> the non-scrub animation is affected (unlike other callbacks which occur <i>after</i> it's affected), so you can do whatever you want in preparation. All ScrollTriggers have a <code>getTrailing()</code> method you can use to get an Array of all ScrollTriggers that precede this one in the updating order according to the current scroll direction. Use the <code>endAnimation()</code> method to force a ScrollTrigger's animation to jump to its end state according to its direction (so if it's going backwards, it'll <code>.progress(0)</code> if it's forward, it'll <code>.progress(1)</code>).
					<pre class="prettyprint">preventOverlaps: (self) => {
  self.getTrailing().forEach((trigger) => {
    trigger.endAnimation();
  });
}</pre>
				</li>
				<li>If <code>fastScrollEnd</code> is a <strong>number</strong>, it will be interpreted as the velocity threshold in pixels-per-second, like <code>fastScrollEnd: 1000</code> would only activate if the velocity is more than 1000 px/second in either direction when leaving the ScrollTrigger's active area.</li>
			</ul>
		</div>
	</section>
	</div>
</div>
</div>


<header>
	<a href="https://greensock.com/scrolltrigger">
		<img class="greensock-icon" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/scroll-trigger-logo-light.svg" width="200" height="64" />
	</a>
</header>
* {
  box-sizing: border-box;
}
.jumps {
  color: dodgerblue;
  z-index: 5;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  position: fixed;
  color: #777;
}
.jumps a {
  color: dodgerblue;
}
body {
  background-color: #222;
  color: #ccc;
  font-size: 17px;
  line-height: 1.4;
  font-weight: 300;
  overscroll-behavior: none;
}
h1, h2 {
  color: white;
  font-weight: 400;
  margin-bottom: 2rem;
}
h1 {
  font-size: 40px;
}
h1, h2, p, li {
  max-width: none;
}

.panel p, .panel li {
  color: #ddd;
  font-weight: 300;
}

.panel {
  font-weight: 300;
  color: white;
  height: auto;
  min-height: 100vh;
}

.container {
  position: relative;
  display: flex;
  flex-flow: column;
  align-items: center;
}

.pin {
  background-color: #111;
  min-height: 100vh;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 94%;
  padding: 0;
  position: fixed;
}

.standard, .features {
  width: 100%;
  padding: 5px;
}
.standard {
  border-bottom: 2px solid #555;

}
.features h2 {
  margin-bottom: 0;
}

.boxes {
  position: relative;
  width: 100%;
  display: flex;
  justify-content: space-between;
}
.box {
  width: 5%;
  position: relative;
  background-color: dodgerblue;
  padding: 1%;
  border-radius: 10px;
  transform: scale(0);
}

.blue {
  background-color: dodgerblue;
  background-image: none;
}
.red {
  background-color: #a90000;
  background-image: none;
}
.purple {
  background-color: #541c61;
  background-image: none;
}
.green {
  background-color: #3c7413;
  background-image: none;
}
.gray {
  background-color: #111;
  background-image: none;
}


#checkboxes {
  padding: 12px;
  background-color: #111;
  text-align: center;
  display: flex;
  justify-content: space-evenly;
  flex-wrap: wrap;
}
#checkboxes input {
  width: 20px;
  height: 20px;
  vertical-align: middle;
}
#checkboxes label {
  white-space: nowrap;
  margin: 0 14px;
}
.panel p code, .panel li code {
  background-color: rgba(0,0,0,0.25);
  color: white;
  padding: 2px 6px;
}
.panel li {
  margin-bottom: 10px;
}

.box-5 {
  visibility: hidden;
}

.explanation {
  position: relative;
  top: 0;
  transform: translateY(-50px);
  text-align: center;
  max-width: 900px;
  margin: 0 auto;
  display: inline-block;
  /*min-width: 80%;*/
  /*left: 50%;*/
}
.explanation-container {
  text-align: center;
  left: 0;
  right: 0;
  top: 100vh;
  bottom: 0;
  position: fixed;
  z-index: 100;
  overflow: visible;
}
.explanation .tab {
  height: 50px;
  font-size: 20px;
  color: white;
  text-align: center;
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
  padding: 0 30px;
  line-height: 50px;
  display: inline-block;
  cursor: pointer;
  transform: translateY(1px);
  border-top: 1px solid #aa70c8;
}
.explanation .content {
  overflow: auto;
  height: calc(100vh - 65px);
  border: 1px solid #555;
}
.explanation .panel {
  height: auto;
  min-height: auto;
  padding: 40px;
}
.explanation h2 {
  margin: 0;
}
.explanation p, .explanation h2, .explanation ul {
  max-width: 800px;
}
.prettyprinted {
  overflow: auto;
  max-width: 80vw;
}
@media (max-height: 500px) {
  .panel h1, .panel h2 {
    font-size: 24px;
  }
  .panel p {
    font-size: 17px;
  }
  #checkboxes input {
    width: 12px;
    height: 12px;
  }
  #checkboxes label {
    font-size: 14px;
    margin: 0 5px;
  }
  .explanation .tab {
    height: 32px;
    line-height: 32px;
    font-size: 18px;
    padding: 0 14px;
  }
  .explanation {
    transform: translateY(-32px);
  }
}
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin);

let boxes = gsap.utils.toArray(".boxes"),
    checkboxPO = document.querySelector("#checkboxPO"),
    checkboxFSE = document.querySelector("#checkboxFSE");

function setup() {
  ScrollTrigger.getAll().forEach(t => {t.scroll(0); t.kill(true); });

  gsap.set(".box", {
    scale: (i, target) => target.classList.contains("box-1") ? 1 : 0
  });
  boxes.forEach((container, i) => {
    let preventOverlaps = i === 1 && checkboxPO.checked && "group1",
        fastScrollEnd = i === 1 && checkboxFSE.checked,
        box = gsap.utils.toArray(".box", container);
    box.pop();
    box.forEach((el, i) => {
      let tl = gsap.timeline({
        scrollTrigger: {
          trigger: ".move-" + (i + 2),
          start: "top center",
          end: "+=100%",
          toggleActions: "play none none reverse",
          preventOverlaps: preventOverlaps,
          fastScrollEnd: fastScrollEnd
        }
      });
      if (i) {
        tl.fromTo(el, {scale: 0}, {scale: 1, duration: 0.5, immediateRender: false, ease: "back"}, 0);
      }
      tl.fromTo(el, {left: "0%"}, {left: "23.75%", duration: 2, immediateRender: false, ease: "power1.inOut"}, i ? "-=0.25" : 0);
      tl.to(el, {scale: 0.6, backgroundColor: "#555", duration: 0.5, opacity: 0.5, ease: "power1.in"});
    });

  });
}

checkboxPO.addEventListener("change", setup);
checkboxFSE.addEventListener("change", setup);

setup();


// explanation tab
let isOpen;
document.querySelector(".explanation .tab").addEventListener("click", () => {
  isOpen = !isOpen;
  gsap.to(".explanation", {
    top: isOpen ? ((window.innerHeight - 65) / window.innerHeight * -100) + "vh" : "0",
    ease: "power4",
    duration: 0.5
  });
});

// jump links
gsap.utils.toArray(".jumps a").forEach((el, i) => {
  el.addEventListener("click", e => gsap.to(window, {scrollTo: i ? 0 : "max", overwrite: true}) && e.preventDefault());
});

// making the code pretty/formatted.
PR.prettyPrint();

External CSS

  1. https://codepen.io/GreenSock/pen/7ba936b34824fefdccfe2c6d9f0b740b
  2. https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css

External JavaScript

  1. https://unpkg.co/gsap@3/dist/gsap.min.js
  2. https://assets.codepen.io/16327/ScrollTrigger.min.js
  3. https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js?lang=css&amp;skin=sunburst
  4. https://unpkg.com/gsap@3/dist/ScrollToPlugin.min.js