CSS animations - How to toggle direction with one keyframes declaration

Viewed 13229

I would like a div to animate to the right when a button is clicked, then animate again to the left when the same button is clicked. I would like to use only one keyframes declaration. Is this possible by swapping classes?

I've tried this in CodePen, but unfortunately, the div snaps back after the first animation, then refuses to animate the second time.

I'm not using CSS transitions because I want to be able to use features of CSS animations like a bouncing effect.

window.addEventListener("load", function() {
  var movedOver = false;

  document.querySelector("button").addEventListener("click", function() {
    var buttonEl = document.querySelector(".one");

    if (movedOver) {
      buttonEl.classList.remove("do-the-slide");
      buttonEl.classList.add("do-the-slide-back");

    } else {
      buttonEl.classList.remove("do-the-slide-back");
      buttonEl.classList.add("do-the-slide");
    }

  });

});
.container {
  position: absolute;
  perspective: 800px;
  >div {
    position: absolute;
    padding: 20px;
    text-align: center;
    width: 100px;
  }
  >div.do-the-slide {
    animation: moveOver 1s ease-out;
  }
  >div.do-the-slide-back {
    animation: moveOver 1s reverse ease-out;
  }
  >.one {
    background: red;
  }
}

button {
  margin-top: 100px;
}

@keyframes moveOver {
  from {
    transform: translateX(0px);
  }
  to {
    transform: translateX(100px);
  }
}
<div class="container">
  <div class="one">One</div>
</div>

<button>Clicky</button>

Codepen: http://codepen.io/anon/pen/Vaadao

3 Answers

I made a pen based on this post and the css-tricks article here codepen

HTML

<h2>Single keyframe-track burger collection</h2>
<p>(each span uses only one track to play for- and backwards)</p>

<div class="wrapper">
  <div class="cell">
    <h3>Default</h3>
    <div>
      <button class="burger">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

  <div class="cell">
    <h3>Merge</h3>
    <div>
      <button class="burger burger--merge">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

  <div class="cell">
    <h3>Rotate</h3>
    <div>
      <button class="burger burger--rotate">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

  <div class="cell">
    <h3>Spin</h3>
    <div>
      <button class="burger burger--spin">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

</div>

CSS

body {
  position: relative;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100vh;
  background: radial-gradient(#5E802B, #2F4016);
  font-family: sans-serif;
  text-align: center;
  h2,
  h3,
  p {
    color: rgb(255, 255, 255);
  }
}
.wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
}

.cell {
  flex-grow: 1;
  flex-basis: 50%;
  margin: 0 20px;
  display: flex;
  flex-direction: column;

  div {
    margin-top: auto;
  }
}
/****************
* Burger Styles *
*****************/
.burger {
  position: relative;
  width: 60px;
  height: 60px;
  padding: 0;
  border: none;
  background-color: transparent;

  &__bar {
    height: 10%;
    border-radius: 2px;
    width: 80%;
    background-color: white;
    display: block;
    position: absolute;
    left: 50%;
    animation-timing-function: ease-in-out;
    animation-play-state: paused;
    animation-duration: .4s;
    animation-fill-mode: both;

    &:nth-child(1) {
      top: 25%;
      transform: translate(-50%, 0%);
    }
    &:nth-child(2) {
      top: 50%;
      transform: translate(-50%, -50%);
    }
    &:nth-child(3) {
      bottom: 25%;
      transform: translate(-50%, 0%);
    }
  }

  &.close .burger__bar {
    animation-direction: reverse;
  }
  &.open .burger__bar {
    animation-direction: normal;
  }
  // default
  &.animate .burger__bar { 
    animation-play-state: running;
    &:nth-child(1) {
      animation-name: default-top;
    }
    &:nth-child(2) {
      animation-name: default-middle;
    }
    &:nth-child(3) {
      animation-name: default-bottom;
    }
  }

  &--merge.animate .burger__bar {
    animation-duration: .6s;
    animation-timing-function: ease;
    &:nth-child(1) {
      animation-name: merge-top;
    }
    &:nth-child(2) {
      animation-name: merge-middle;
    }
    &:nth-child(3) {
      animation-name: merge-bottom;
    }
  }

  &--rotate.animate .burger__bar {
    &:nth-child(1) {
      animation-name: rotate-top;
    }
    &:nth-child(2) {
      animation-name: rotate-middle;
    }
    &:nth-child(3) {
      animation-name: rotate-bottom;
    }
  }

  &--spin.animate .burger__bar {
    animation-duration: 1s;
    animation-timing-function: ease;

    &:nth-child(1) {
      animation-name: spin-top;
    }
    &:nth-child(2) {
      animation-name: spin-middle;
    }
    &:nth-child(3) {
      animation-name: spin-bottom;
    }
  }
}

/*__________________
       DEFAULT
____________________*/
@keyframes default-top {
  50%  {
    top:50%;
    transform: translate(-50%, -50%) rotate(0);
  }
  100% {
    top:50%;
    transform: translate(-50%, -50%) rotate(-45deg);
  }
}
@keyframes default-middle {
  50%, 100% {visibility:hidden;}
}
@keyframes default-bottom {
  0%   {}
  25%  {}
  50%  {
    bottom:50%;
    transform: translate(-50%, -50%) rotate(0deg);
  }
  100% {
    bottom:50%;
    transform: translate(-50%, 50%) rotate(45deg);
  }
}
/*__________________
       MERGE
____________________*/
@keyframes merge-top {
  20%  {
    top:50%;
    transform: translate(-50%, -50%) rotate(0) scale(1, 1);
    //width: 100%;
  }
  50%  {
    transform: translate(-50%, -50%) rotate(0) scale(0.2, 1);
    //width: 20%;
  }
  60% {
    transform: translate(-50%, -50%) rotate(-45deg) scale(0.2, 1);
    //width: 20%;
  }
  100% {
    top:50%;
    transform: translate(-50%, -50%) rotate(-45deg) scale(1, 1);
    //width: 100%;
  }
}
@keyframes merge-middle {
  50%  {
    transform: translate(-50%, -50%) scale(0, 1);
  }
  51%{visibility:hidden;}
  99% {
    transform: translate(-50%, -50%) scale(0, 1);
  }
  100% {visibility:hidden;}
}
@keyframes merge-bottom {
  20%  {
    bottom:40%;
    transform: translate(-50%, -50%) rotate(0deg) scale(1, 1);
  }
  50%  {
    transform: translate(-50%, -50%) rotate(0deg) scale(0.2, 1);
  }
  60% {
    transform: translate(-50%, -50%) rotate(45deg) scale(0.2, 1);
  }
  100% {
    bottom:40%;
    transform: translate(-50%, -50%) rotate(45deg) scale(1, 1);
  }
}
/*__________________
       ROTATE
____________________*/
@keyframes rotate-top {
  50%  {
    top:50%;
    transform: translate(-50%, -50%) rotate(0deg);
  }
  100% {
    top:50%;
    transform: translate(-50%, -50%) rotate(45deg);
  }
}
@keyframes rotate-middle {
  50%  {transform: translate(-50%, -50%) rotate(0deg);}
  100% {transform: translate(-50%, -50%) rotate(135deg);}
}
@keyframes rotate-bottom {
  50%  {
    bottom:50%;
    transform: translate(-50%, 50%) rotate(0deg);
  }
  100% {
    bottom:50%;
    transform: translate(-50%, 50%) rotate(45deg);
  }
}

/*__________________
       SPIN
____________________*/
@keyframes spin-top {
  50%  {
    top:50%;
    transform: translate(-50%, -50%) rotate(0deg);
  }
  75% {
    transform: translate(-50%, -50%) rotate(360deg);
  }
  100% {
    top:50%;
    transform: translate(-50%, -50%) rotate(315deg);
  }
}
@keyframes spin-middle {
   50%  {visibility:hidden;}
  100% {visibility:hidden;}
}
@keyframes spin-bottom {
  50%  {
    bottom:50%;
    transform: translate(-50%, 50%) rotate(0deg);
  }
  75% {
    transform: translate(-50%, 50%) rotate(450deg);
  }
  100% {
    bottom:50%;
    transform: translate(-50%, 50%) rotate(405deg);
  }
}

Javascript

burgers = document.querySelectorAll('.burger');
for (let i = 0; i < burgers.length; i++) {
     burgers[i].addEventListener("click", function(){
       this.classList.remove("animate");

       var bars = this.querySelectorAll('.burger__bar');
       for (let i = 0; i < bars.length; i++) {
         // Reset span animations - https://css-tricks.com/restart-css-animation/
         void bars[i].offsetWidth;
       }

       if (this.classList.contains("open")){
         this.classList.remove("open");
         this.classList.add("close");
       } else {
         this.classList.remove("close");
         this.classList.add("open");
       }  
       this.classList.add("animate");
    });
};
Related