DEV Community

Cover image for Using event listeners to create better animations
Cyd
Cyd

Posted on • Updated on

Using event listeners to create better animations

If you want to go beyond the standard hover, focus and click animations you've interacted with the right article! This is all about turning user input into better animations.

Event listeners

There are A LOT of event listeners in JavaScript, some of them you've probably heard of, most of them you'll probably never need or use. My favourite events to use are touch/mouse and scroll events. I am also trying to understand the devicemotion events but haven't come very far, if anyone has any reading tips let me know please!

In the first example I'm using the mouseover event to determine the mouse's position (x y coordinates) on the screen.

Example 1 Skewing on mouseover

An example of how I used this on my portfolio:
An example of how I used this on my portfolio

The HTML is quite basic:

<div class="skew">
  <div class="skew__item">
    <img src="https://pbs.twimg.com/media/BcvmxibIYAAH8a3.jpg" /> 
    <!-- TIP: always use an image you really enjoy
    looking at for codepens 😻 -->
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

The JavaScript gets a bit more complicated:

const skewItemContainer = document.querySelector('.skew'); 
// Get the container element to add the mouseover 
// event and css variable to
const skewItem = document.querySelector('.skew__item').getBoundingClientRect();
// getBoundingClientRect() returns an object with the
// coordinates and width, height etc of an element

const imageCenterX = skewItem.left + skewItem.width / 2;
const imageCenterY = skewItem.top + skewItem.height / 2;
// Calculating the center coordinates for the image

// Add the event to the container around the image 
// (more room to mouseover == more better😎)
skewItemContainer.addEventListener("mousemove", function(e) {
  // the event, e, is an object with a lot of
  // information, I only use clientX and clientY
  // for this animation

  const clientX = e.clientX;
  const clientY = e.clientY;
  // Get x and y positions of the 
  const xCalc = (clientX - imageCenterX) * 0.000001;
  // increasing 0.000001 will make the animation more pronounced
  const yCalc = (clientY - imageCenterY) * 0.000001;
  // Subtracting the X & Y coordinates by the image
  // center ensures that the center of the image 
  // (where ever it may be positioned on the page) is 0, 0 🧐

  skewItemContainer.style.setProperty("--x-translate", `${xCalc}`);
  skewItemContainer.style.setProperty("--y-translate", `${yCalc}`);
  // using setProperty to add css variables to the container element
});
Enter fullscreen mode Exit fullscreen mode

Okay I lied a little bit, I'm not actually using the skew() transformation function, I'm using matrix3d(), it gives you more control. This matrix3d transform is complicated AF, I found out you could create a skew like effect by continuously trying different values.

.skew {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  // Centering the element with flexbox ❤️
  background: black;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  --x-translate: 0;
  --y-translate: 0;
  // creating defaults for the css variables
  &:hover {
    .skew__item {
      transform: matrix3d(1, 0, 0.00, var(--x-translate), 0.00, 1, 0.00, var(--y-translate), 0, 0, 1, 0, 0, 0, 0, 1); 
      // By adding the transform only on hover you 
      // make sure that when the user hovers out it
      // will return to it's non transformed standard
      // position, which saves you some code ;)
    }
  }
  &__item {
    width: 60vw;
    max-width: 500px;
    transition: 0.4s cubic-bezier(0.64, 0.57, 0.67, 1.53);
    img {
      width: 100%;
      height: 100%;
      object-fit: cover; // ❤️❤️❤️❤️ object-fit
    }
    &:hover {
      transition: 0.4s ease-out;
      // adding a different animation on hover than
      // hover out makes animations more playful
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Example 2 Scaling up on scroll

An example of how I used this on my portfolio:
An example of how I used this on my portfolio

In the codepen I used three videos, this is absolutely not necessary! In the code examples here I'll show you how to do it with just one video.

Big shout out to the company I work at (Matise) who created this video and the design shown in the video. 👏

<section class="scale-video">
  <video class="video" autoplay muted loop playsinline src="https://cdn.matise.nl/content/uploads/2018/03/20092418/02_bbbSingleProduct.mp4"></video>
</section>
<section class="row">
  <h1>Scroll!!</h1>
</section>
Enter fullscreen mode Exit fullscreen mode
const videoContainer = document.querySelector('.scale-video');
const video = document.querySelector('.video');
// Create variables for the container and the video for easy access

window.addEventListener('scroll', function(e) {
  let scrollY = window.scrollY  / window.innerHeight * 5;
  // Create a calculation to base the scale amount on

  // Create min and max values for the scaling:
  if (scrollY <= 0.3) {
    scrollY = 0.3;
    // We don't want the video to become smaller than (scale(0.3))
  }
  if (scrollY >= 1) {
    scrollY = 1;
    // We don't want the video to become bigger than (scale(1))
  }
  videoContainer.style.setProperty('--scale', `${ scrollY }`);
  // Set css variable on the video container
});
Enter fullscreen mode Exit fullscreen mode
:root {
  // setting defaults can also be done on the root pseudo class
  // https://tympanus.net/codrops/css_reference/root/
  --scale: 0.3;
}

.scale-video {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  // Center the content with flexbox
  width: 100vw;
  box-sizing: border-box;
  height: 300vh;
  // The height here is quite outrageous, haha.
  // It's to make sure that you have enough place
  // to scroll to let the animation do its thing.
  overflow: hidden;
  background: black;
}

.video {
  position: fixed;
  top: 0; 
  transition: 1s ease-out;
  width: 100vw;
  z-index: 2;
  overflow: hidden;
  object-fit: cover;
  transform: scale(var(--scale));
  // Add the scale via css variable
}

Enter fullscreen mode Exit fullscreen mode

That was it for now, let me know if you've ever made animations like these. I'd love to see them!

Top comments (8)

Collapse
 
bcdbuddy profile image
Babacar Cisse DIA

hey nice animations out there! I love them
but did you thought about debouncing the scroll event listener ? From experience I know this type of events can be very called a huge amount of time

// from npm
import debounce from 'lodash.debounce';

// or self implementation
function debounce(callback, delay){
  var timer;
  return function(){
      var args = arguments;
      var context = this;
      clearTimeout(timer);
      timer = setTimeout(function(){
          callback.apply(context, args);
      }, delay)
  }
}

window.addEventListener('scroll', debounce(function(e) {
  let scrollY = window.scrollY  / window.innerHeight * 5;
  // Create a calculation to base the scale amount on

  // Create min and max values for the scaling:
  if (scrollY <= 0.3) {
    scrollY = 0.3;
    // We don't want the video to become smaller than (scale(0.3))
  }
  if (scrollY >= 1) {
    scrollY = 1;
    // We don't want the video to become bigger than (scale(1))
  }
  videoContainer.style.setProperty('--scale', `${ scrollY }`);
  // Set css variable on the video container
}));
Collapse
 
cloudpower97 profile image
Claudio Cortese

Nice post!
Didn't know about matrix3d. 🙂
However, have you considered using throttling to improve the performance? 😊

Collapse
 
cydstumpel profile image
Cyd

You’re right, i should definitely at least add a request keyframe or something 🤦🏽‍♀️

Collapse
 
jinglescode profile image
Jingles (Hong Jing)

Thanks for the code demos. Impressive

Collapse
 
vaibhavkhulbe profile image
Vaibhav Khulbe

Awesome work with that Pen! Love it!

Collapse
 
cydstumpel profile image
Cyd

Glad you liked it, Vaibhav!

Collapse
 
omarsteam profile image
omar-steam • Edited

Awesome post. That should definitely be helpful.

Collapse
 
jdmedlock profile image
Jim Medlock

I love what you've done. Thanks for sharing these techniques!