Velocity

Elder Project

by Grzegorz Matyszewski

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/ScrollTrigger.min.js"></script>
<div class="bars">
  <div class="bar-wrap">Scroll Velocity:
    <div class="bar" id="scroll-velocity">0</div>
  </div>
  <div class="bar-wrap">Mouse Velocity:
    <div class="bar" id="mouse-velocity">0</div>
  </div>
</div>

<div id="content">
  <h1>
  Shining
    <span>Shining</span>
    <span>Shining</span>
    <span>Shining</span>
    <span>Shining</span>
    <span>Shining</span>
  </h1>
</div>

CSS

body {
  background: #F8F7F7;
  pointer-events: none;
}

.bars {
  position: fixed;
  top: 0;
}

.bar-wrap {
  display: flex;
  gap: 10px;
}

.bar {
  height: 20px;
  background: red;
  width: 0;
}

#content {
  height: 400vh;
}

h1 {
  font-family: "Arial Narrow", sans-serif;
  text-transform: uppercase;
  position: relative;
  margin-top: 392px;
  font-size: 18vw;
  letter-spacing: -0.02em;
  color: #1B1B1B;
  font-style: italic;
}

h1 span {
  position: absolute;
  left: 0;
  z-index: -1;
}

h1 span:nth-of-type(1) { color: #1B7DB6; }
h1 span:nth-of-type(2) { color: #548068; }
h1 span:nth-of-type(3) { color: #BDD5E3; }
h1 span:nth-of-type(4) { color: #EDB548; }
h1 span:nth-of-type(5) { color: #D54335; }

JavaScript

const maxVelocity = 10;
const velocityFactor = 800;
const shift = 4;
var velocity = 0;

const tl = gsap.timeline({
  paused: true
});
tl.fromTo('h1 span', {
  xPercent: (index, target, targets) => -shift * (targets.length - index),
}, {
  xPercent: (index, target, targets) => shift * (targets.length - index),
  ease: 'none',
});
tl.progress(0.5);


ScrollTrigger.create({
  onUpdate: self => {
    velocity = Math.max(-maxVelocity, Math.min(maxVelocity, self.getVelocity() / velocityFactor));
    gsap.killTweensOf(window, "velocity");
    gsap.to(window, {
      velocity: 0,
      duration: 2,
      ease: 'power3',
      overwrite: true,
    });
  },
});


let lastMouseX = 0;
let lastMouseY = 0;
let lastTime = Date.now();
var mouseVelocity = 0;

document.addEventListener('mousemove', (event) => {
  const currentTime = Date.now();
  const elapsed = currentTime - lastTime;
  lastTime = currentTime;

  const velocityX = (event.clientX - lastMouseX) / elapsed;
  const velocityY = (event.clientY - lastMouseY) / elapsed;

  gsap.killTweensOf(window, "mouseVelocity");
  gsap.to(window, {
    duration: 1,
    ease: 'power3',
    mouseVelocity: Math.sqrt(velocityX * velocityX + velocityY * velocityY) / 10,
  });

  lastMouseX = event.clientX;
  lastMouseY = event.clientY;
});


function update() {
  document.getElementById('mouse-velocity').innerHTML = mouseVelocity;
  gsap.set('#mouse-velocity', {
    width: 10 * mouseVelocity
  });
  document.getElementById('scroll-velocity').innerHTML = velocity;
  gsap.set('#scroll-velocity', {
    width: 10 * velocity
  });

  tl.progress(Math.min(1, Math.max(-1, 0.5 + (velocity / maxVelocity) / 2 + mouseVelocity / 2)));
}

function loop() {
  if (mouseVelocity > 0) {
    mouseVelocity *= 0.95;
  }
  
  update();

  requestAnimationFrame(() => loop());
}

requestAnimationFrame(() => loop());