Using event listeners to create better animations
Tags: css, javascript, tutorial, webdev
Published: 2020-01-18 16:40:11

---
title: Using event listeners to create better animations
published: true
description: Using event listeners like mouse position and scroll to create unique animations
tags: css, js, tutorial, webdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/lz6pd8rwusd38v9joa64.gif
---

**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](https://developer.mozilla.org/en-US/docs/Web/Events) 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](https://cydstumpel.nl/):
![An example of how I used this on my portfolio](https://thepracticaldev.s3.amazonaws.com/i/4bwcyzcxnjs5sqbaeg29.gif)

The HTML is quite basic:
```html
<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>
```

The JavaScript gets a bit more complicated:
```js
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
});
```
Okay I lied a little bit, I'm not actually using the [`skew()`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skew) transformation function, I'm using [`matrix3d()`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/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. 

```scss
.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
    }
  }
}

```
{% codepen https://codepen.io/Sidstumple/pen/jJaWBr %}


### Example 2 Scaling up on scroll
An example of how I used this on [my portfolio](https://cydstumpel.nl/case/rideout/):
![An example of how I used this on my portfolio](https://thepracticaldev.s3.amazonaws.com/i/lz6pd8rwusd38v9joa64.gif)

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](https://www.matise.nl/)) who created this video and the design shown in the video. 👏
```html
<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>
```
```js
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
});
```

```scss
: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
}

```

{% codepen https://codepen.io/Sidstumple/pen/pYexRJ %}

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