inflation-explorer/shared/js/scrollyteller.js (70 lines of code) (raw):

import { supportsSticky } from "./util.js" class ScrollyTeller { constructor(config) { this.isMobile = window.innerWidth < 740; this.triggerTop = (!this.isMobile) ? config.triggerTop : config.triggerTopMobile; this.scrollInner = config.parent.querySelector(".scroll-inner"); this.scrollText = config.parent.querySelector(".scroll-text"); this.scrollWrapper = config.parent.querySelector(".scroll-wrapper"); this.lastScroll = null; this.lastI = null; this.triggerPoints = []; this.textBoxes = [].slice.apply(this.scrollText.querySelectorAll(".scroll-text__inner")); this.transparentUntilActive = config.transparentUntilActive; this.scrollWrapper.style.height = this.textBoxes.length * 100 + "vh"; if(this.transparentUntilActive) { config.parent.classList.add("transparent-until-active"); } } checkScroll() { if(this.lastScroll !== window.pageYOffset) { const bbox = this.scrollText.getBoundingClientRect(); if(!supportsSticky) { if(bbox.top <= 0 && bbox.bottom >= window.innerHeight) { this.scrollInner.classed("fixed-top", true); this.scrollInner.classed("absolute-bottom", false); this.scrollInner.classed("absolute-top", false); } else if(bbox.top <= 0) { this.scrollInner.classed("fixed-top", false); this.scrollInner.classed("absolute-bottom", true); this.scrollInner.classed("absolute-top", false); } else { this.scrollInner.classed("fixed-top", false); this.scrollInner.classed("absolute-bottom", false); this.scrollInner.classed("absolute-top", true); } } if(bbox.top < (window.innerHeight*(this.triggerTop)) && bbox.bottom > window.innerHeight/2) { const i = Math.floor(Math.abs(bbox.top - (window.innerHeight*(this.triggerTop)))/bbox.height*this.textBoxes.length); if(i !== this.lastI) { this.lastI = i; this.doScrollAction(i); if(this.transparentUntilActive) { this.textBoxes.forEach((el, j) => { if(j <= i) { el.style.opacity = "1"; } else { el.style.opacity = "0.25"; } }); } } } this.lastScroll = window.pageYOffset; } window.requestAnimationFrame(this.checkScroll.bind(this)); } doScrollAction(i) { const trigger = this.triggerPoints.find(d => d.num === i+1); if(trigger) { trigger.do(); } } watchScroll() { window.requestAnimationFrame(this.checkScroll.bind(this)); } addTrigger(t) { this.triggerPoints.push(t); } } export default ScrollyTeller