Search code examples
csscss-animationscss-transformskeyframe

Keyframe animation issue with transform-origin


I'm trying to build an animation for opening a hamburger menu. I am struggling to get the hamburger to the x. In theory I seem to be doing everything right but the result tells me otherwise xD. I've been playing around with the translate values and also the transform-origin but it all behaves in (for me) unexpected ways. Could someone have a look and help me out?

My Example: https://codepen.io/aki-sol/pen/RwMoEJQ

<svg width="48" height="48" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg">
  <rect class="top" y="8.5" width="48" height="3.875" fill="blue" />
  <rect class="middle" y="22.0625" width="48" height="3.875" fill="blue" />
  <rect class="bottom" y="35.625" width="48" height="3.875" fill="blue" />
</svg>
svg:hover .top {
  animation-name: top;
  animation-duration: 1.5s;
  animation-timing-function: ease-in-out;
  /*transform-origin: 25% 25%;*/
  animation-fill-mode: forwards;
}
svg:hover .middle {
  animation-name: middle;
  animation-duration: 1.5s;
  animation-timing-function: linear;
  transform-origin: center center;
  animation-fill-mode: forwards;
}
svg:hover .bottom {
  animation-name: bottom;
  animation-duration: 1.5s;
  animation-timing-function: ease-in-out;
  /*transform-origin: 25% 25%;*/
  animation-fill-mode: forwards;
}

@keyframes top {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(30%);
  }
  100% {
    transform: rotate(45deg) translateY(-25%);
  }
}
@keyframes middle {
  50% {
    opacity: 0;
  }
  100% {
    opacity: 0;
  }
}
@keyframes bottom {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-30%);
  }
  100% {
    transform: rotate(-45deg);
  }
}

Goal (first example): https://codepen.io/vineethtrv/pen/VYRzaV

Thanks!


Solution

  • I have changed a little bit the dimensions to make the values easier to calculate.

    So, svg width is 48, so I set the transform-origin x value at 24, the center.

    For top, y is 8 and height is 4, so the center is at 8 + (4 / 2) = 10.

    The corrected demo goes like this:

    svg:hover .top {
      animation-name: top;
      animation-duration: 1.5s;
      animation-timing-function: ease-in-out;
      transform-origin: 24px 10px;
      animation-fill-mode: forwards;
    }
    svg:hover .middle {
      animation-name: middle;
      animation-duration: 1.5s;
      animation-timing-function: linear;
      transform-origin: center center;
      animation-fill-mode: forwards;
    }
    svg:hover .bottom {
      animation-name: bottom;
      animation-duration: 1.5s;
      animation-timing-function: ease-in-out;
      transform-origin: 24px 38px;
      animation-fill-mode: forwards;
    }
    
    @keyframes top {
      0% {
        transform: translateY(0);
      }
      50% {
        transform: translateY(30%);
      }
      100% {
        transform:  translateY(30%) rotate(45deg);
      }
    }
    @keyframes middle {
      50% {
        opacity: 0;
      }
      100% {
        opacity: 0;
      }
    }
    @keyframes bottom {
      0% {
        transform: translateY(0);
      }
      50% {
        transform: translateY(-30%);
      }
      100% {
        transform: translateY(-30%) rotate(-45deg);
      }
    }
    <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
      <rect class="top" y="8" width="48" height="4" fill="blue" />
      <rect class="middle" y="22" width="48" height="4" fill="blue" />
      <rect class="bottom" y="36" width="48" height="4" fill="blue" />
    </svg>