JSFiddle - React, Tailwind, and code Playground

Using CSS variables to control start and end of CSS keyframes animation.

by Travis Almand

HTML

<div id="top" class="target" data-loc="0,-100px,0"></div>
<div id="right" class="target" data-loc="100px,0,0"></div>
<div id="bottom" class="target" data-loc="0,100px,0"></div>
<div id="left" class="target" data-loc="-100px,0,0"></div>
<div id="box"></div>

CSS

:root {
  --start: translate3d(0, 0, 0);
  --end: translate3d(0, 0, 0);
}

.target {
  background: gainsboro;
  bottom: 0;
  cursor: pointer;
  height: 50px;
  left: 0;
  margin: auto;
  position: absolute;
  right: 0;
  top: 0;
  width: 50px;
}
#top { top: -200px; }
#right { right: -200px; }
#bottom { bottom: -200px; }
#left { left: -200px; }

#box {
  background: black;
  bottom: 0;
  height: 20px;
  left: 0;
  margin: auto;
  position: absolute;
  right: 0;
  top: 0;
  width: 20px;
}
#box.animate {
  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-name: animate;
  animation-timing-function: linear;
}

@keyframes animate {
  0% {
    transform: var(--start);
  }
  50% {
    transform: translate3d(0, 0, 0);
  }
  100% {
    transform: var(--end);
  }
}

JavaScript

var $box = document.querySelector('#box');
var $top = document.querySelector('#top');
var $right = document.querySelector('#right');
var $bottom = document.querySelector('#bottom');
var $left = document.querySelector('#left');
var $targets = document.querySelectorAll('.target');

function animate (event) {
	var $this = event.currentTarget
	var current = window.getComputedStyle($box).getPropertyValue('transform');
  
  if (!$this.classList.contains('active')) {
    for(var i = 0; i < $targets.length; i++) {
      $targets[i].classList.remove('active');
    }
    $this.classList.add('active');
    
    $box.classList.remove('animate');
    void $box.offsetWidth;
    $box.classList.add('animate');
    document.documentElement.style.setProperty('--start', current);
    document.documentElement.style.setProperty('--end', 'translate3d(' + $this.dataset.loc + ')');
  }
}

$top.addEventListener('click', animate);
$right.addEventListener('click', animate);
$bottom.addEventListener('click', animate);
$left.addEventListener('click', animate);