JSFiddle - React, Tailwind, and code Playground

by RAX7

HTML

<canvas id="sequence" width="320" height="240"></canvas>
<canvas id="scroll" width="320" height="240" style="top: 260px"></canvas>

CSS

html {
  height: 100%;
}
body {
  height: 600%;
}

canvas {
  position: fixed;
  background-color: #ffffff;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cg fill='%23969696' fill-opacity='0.4'%3E%3Cpath fill-rule='evenodd' d='M0 0h4v4H0V0zm4 4h4v4H4V4z'/%3E%3C/g%3E%3C/svg%3E");
}

JavaScript

const framesLink = 'https://i.imgur.com/QvNKsCf.png';
const frameWidth = 320;
const frameHeight = 240;

function loadImg(src) {
  return new Promise(resolve => {
    const img = new Image();
    img.addEventListener('load', () => resolve(img));
    img.crossOrigin = 'Anonymous';
    img.src = src;
  });
}

loadImg(framesLink).then((image) => {
	play(image);
  scroll(image);
});

function play(image) {
	const fps = 24;
  const frames = 30;
  const duration = frames / fps * 1000;
	const canvas = document.querySelector('#sequence');
  const ctx = canvas.getContext('2d');

	let begin = 0;
  function loop(now) {
  	begin = begin || now;
  	const elapsed = now - begin;
    const progress = elapsed / duration % 1;
    const nf = Math.round(progress * (frames - 1));
    const offset = frameWidth * nf;

    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.drawImage(image, offset, 0, frameWidth, frameHeight, 0, 0, frameWidth, frameHeight);
  	requestAnimationFrame(loop);
  }
  requestAnimationFrame(loop);
}

function scroll(image) {
  const frames = 30;
	const canvas = document.querySelector('#scroll');
  const ctx = canvas.getContext('2d');
  
  onScroll();
  document.addEventListener('scroll', onScroll);
  
  function onScroll() {
    const scrolled = (document.documentElement.scrollTop || document.body.scrollTop) /
      ((document.documentElement.scrollHeight || document.body.scrollHeight) - document.documentElement.clientHeight);

    const nf = Math.round(scrolled * (frames - 1));
    const offset = frameWidth * nf;

    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.drawImage(image, offset, 0, frameWidth, frameHeight, 0, 0, frameWidth, frameHeight);
  }
}