Transitions & Animation

Transitions

transition: all .15s ease;
.box {
    background-color: navy;
    transition: background-color 1s ease;
}
.box:hover {
    background-color: aqua;
}

CSS Fade-In Transition

.box {
  transition: opacity 500ms ease-in-out;
  opacity: 0;
}
.box:hover {
  opacity: 1;
}

Transition via JavaScript

DOM Events

  • transitionend

JavaScript Fade-In Transition

const element = document.createElement('div');
element.style.opacity = '0';
document.body.append(element);
getComputedStyle(element).opacity;
element.style.transition = 'opacity 500ms ease-in-out';
element.style.opacity = '1';

The getComputedStyle(element).opacity is essential here, otherwise the rendering engine never sees the opacity of 0.

JavaScript Fade-Out Transition

const animation = element.addEventListener('transitionend', (event) => {
  if (event.target !== element) return;
  element.remove();
});

Animation

Keyframes

@keyframes my-animation {
  0%   {  }
  100% {  }
}

Applying the animation

animation-name: my-animation;
animation-duration: 400ms;
animation-timing-function: linear;
animation-delay: 0;
animation-iteration-count: infinite;

animation-fill-mode: forwards;
animation-fill-mode: both;

animation-direction: alternate;

animation-play-state: paused;
animation-play-state: running;

Shorthand

animation: name duration timing-function delay iteration-count direction fill-mode;

Example

.spotlight-pulse-animation {
  animation-name: pulse-animation;
  animation-duration: 400ms;
  animation-timing-function: linear;
  animation-delay: 0;
  animation-iteration-count: infinite;
}

@keyframes pulse-animation {
  0%   { transform: scale(1); }
  25%  { transform: scale(1.5); }
  75%  { transform: scale(0.5); }
  100% { transform: scale(1); }
}

Animation via JavaScript

DOM Events

  • animationstart
  • animationiteration
  • animationend
  • finish

CSS Fade-In Animation

@keyframes fade-in {
  from { opacity: 0; }
}
@keyframes fade-out {
  to { opacity: 0; }
}
const element = document.createElement('div');
element.style.animation = 'fade-in 500ms ease-in-out';
document.body.append(element);

JavaScript Fade-Out Animation

element.style.animation = 'fade-out 500ms ease-in-out';
element.addEventListener('animationend', (event) => {
  if (event.target !== element) return;
  element.remove();
});

CSS Offset Path (Motion Path)

Example

#motion-demo {
  offset-path: path("M20,20 C20,100 200,0 200,100");
  animation: move 3000ms infinite alternate ease-in-out;
  width: 40px;
  height: 40px;
  background: cyan;
}

@keyframes move {
  0% {
    offset-distance: 0%;
  }
  100% {
    offset-distance: 100%;
  }
}
.modal {
  transition: 1s;
  offset-path: path("M 250,100 S -300,500 -700,-200");
}
.modal.closed {
  offset-distance: 100%;
}

Web Animations API

element.animate(
  { opacity: '0.2' },
  { duration: 500, easing: 'ease-in-out', fill: 'forwards' }
);
  • animation.commitStyles();
  • animation.cancel();

JavaScript Fade-In via Web Animation API

const element = document.createElement('div');
document.body.append(element);
element.animate(
  { opacity: '0', offset: 0 },
  { duration: 500, easing: 'ease-in-out' }
);

JavaScript Fade-Out via Web Animation API

const fade_out_animation = element.animate(
  { opacity: '0' },
  { duration: 500, easing: 'ease-in-out' }
);

// via promise
fade_out_animation.finished.then(() => element.remove());

// via event
fade_out_animation.onfinish = () => element.remove();
const fade_out_animation = element.animate(
  { opacity: '0.2' },
  { duration: 500, easing: 'ease-in-out', fill: 'forwards' }
);
function animateTo(element, keyframes, options) {
  const animation = element.animate(
    keyframes, 
    { ...options, fill: 'both' }
  );
  animation.addEventListener('finish', () => {
    animation.commitStyles();
    animation.cancel();
  });
  return animation;
}
document.getAnimations();

Animating height

.content {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 500ms;
}

.content[aria-hidden="false"] {
  grid-template-rows: 1fr;
}

.content > div {
  overflow: hidden;
}

Optimising Animations

Best properties in terms of performance to animate:

  • opacity
  • transform

Worst properties to animate in terms of performance (require repaint for every frame):

  • top
  • bottom
  • left
  • right

Further enhancements

will-change: transform;

Box-shadow

<div class="real-box-shadow">Real Box Shadow</div>
.real-box-shadow {
  box-shadow: 0 0.125rem 0.25rem 0 rgba(0,0,0,0.2);
  transition: transform 1s ease, box-shadow 1s ease;
}
.real-box-shadow:hover {
  transform: translateY(-0.3rem) scale(1.02);
  box-shadow: 0 0.5rem 0.7rem -0.25rem rgba(0,0,0,0.5);
}
Real Box Shadow
<div class="pseudo-box-shadow">Pseudo Box Shadow</div>
.pseudo-box-shadow {
  position: relative;
  box-shadow: 0 0.125rem 0.25rem 0 rgba(100,100,100,0.2);
  transition: transform 1s ease;
}
.pseudo-box-shadow:hover {
  transform: translateY(-0.3rem) scale(1.02);
}
.pseudo-box-shadow::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  opacity: 0;
  box-shadow: 0 0.5rem 0.7rem -0.25rem rgba(100,100,100,0.5);
  transition: opacity 1s ease;
}
.pseudo-box-shadow:hover::after {
  opacity: 1;
}
Pseudo Box Shadow

Easing Curves

.particle {
  transition: transform 200ms linear;
  transition: transform 200ms ease;
  transition: transform 200ms ease-in;
  transition: transform 200ms ease-in-out;
  transition: transform 375ms cubic-bezier(0.4, 0, 0.2, 1);
  transition: transform 200ms cubic-bezier(0, .8, .13, 1);
}