javascripthtmlcsscss-animationscss-transitions

Animate transform-origin while maintaining previous rotation position


I am trying to animate a "left" to "right"-side rotation using transform-origin. More specifically, I would like to maintain the left side's rotation position when the transform-origin is switched. Is there a way for me to preserve this positioning despite the rotation being relative to the current origin?

For example,
(1) when left is executed -> [\].
(2) when right is executed -> [-], while maintaining left side previous position.

    function rotate(direction) {
      const rotatedBox = document.getElementsByClassName("rotatedBox")[0]
      if (direction === "right") {
        rotatedBox.classList.remove("left")
        rotatedBox.classList.add("right")
      } else if (direction === "left") {
        rotatedBox.classList.remove("right")
        rotatedBox.classList.add("left")
      }
    }
    .bigBox {
      border: solid 1px red;
      height: 80vh;
      width: 70vw;
      position: relative;
    }

    .rotatedBox {
      border: solid 1px greenyellow;
      background-color: teal;
      height: 10px;
      position: absolute;
      bottom: 0;
      width: 100%;
      transition: transform-origin 0.2s ease-in-out;
    }

    .rotatedBox.right {
      transform-origin: 0% 50% 0px;
      transform: rotate(-10deg);
    }

    .rotatedBox.left {
      transform-origin: 100% 50% 0px;
      transform: rotate(10deg);
    }

    <button onclick="rotate('left')">left</button>
    <button onclick="rotate('right')">right</button>
    <div class="bigBox">
      <div class="rotatedBox"></div>
    </div>

JsFiddle link: https://jsfiddle.net/a75sqvwx/18/


Solution

  • Wrap your rotated element in a container that’s positioned so that the “fixed” edge (in your case, the left side) stays in place. Then apply the rotation on the inner element. This way, the container provides a stable reference point while the inner element rotates.

    function rotate(direction) {
      const box = document.querySelector('.rotatedBox');
      if (direction === 'right') {
        box.classList.remove('left');
        box.classList.add('right');
      } else if (direction === 'left') {
        box.classList.remove('right');
        box.classList.add('left');
      }
    }
    .bigBox {
      border: 1px solid red;
      height: 80vh;
      width: 70vw;
      position: relative;
    }
    
    /* The wrapper fixes the left edge (using left:0) */
    .rotatedWrapper {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 300px;    /* Fixed width for demonstration */
      height: 10px;
      overflow: visible;
    }
    
    .rotatedBox {
      border: 1px solid greenyellow;
      background-color: teal;
      width: 100%;
      height: 100%;
      transition: transform 0.2s ease-in-out;
      /* Always rotate about the left edge */
      transform-origin: left center;
    }
    
    /* For a “right” rotation, rotate slightly in one direction */
    .rotatedBox.right {
      transform: rotate(-10deg);
    }
    
    /* For a “left” rotation, rotate in the opposite direction */
    .rotatedBox.left {
      transform: rotate(10deg);
    }
    <button onclick="rotate('left')">left</button>
    <button onclick="rotate('right')">right</button>
    
    <div class="bigBox">
      <div class="rotatedWrapper">
        <div class="rotatedBox"></div>
      </div>
    </div>