Search code examples
htmlcss3dcss-animationscss-transforms

Rotate child div perpendicular to parent div in 3D space


html {
  font-size: 0.7rem;
}
div div {
  top: 5rem;
  background-color: #444;
  transform-origin: top center;
}

.rotate_x {
  animation-name: rotate_x;
}
@keyframes rotate_x {
  0% {
    transform: rotateX( 0deg );
  }
  100% {
    transform: rotateX( 360deg );
  }
}

.rotate_y {
  animation-name: rotate_y;
}
@keyframes rotate_y {
  0% {
    transform: rotateY( 0deg );
  }
  100% {
    transform: rotateY( 180deg );
  }
}
<style>
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    animation-duration: 5s;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
  }
  html, body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    width: 100%;
  }
  body {
    perspective: 5rem;
  }
  div {
    position: relative;
    top: -1.5rem;
    width: 5rem;
    height: 5rem;
    background-color: #222;
    border-radius: 0.5rem;
  }
</style>

<div class='rotate_y'>
  <div class='rotate_x'>
  </div>
</div>

The gray bottom div doesn't appear to be rotating out perpendicular to the black top div as intended. Instead the bottom gray div appears to shrink until it has a negative value and gets inverted. The desired result is for the bottom gray div to rotate up as if on a hinge; making a 'L' shape in 3D space before it comes all the way up and flips around to do the same on the other side. As the parent div rotates 360 degrees.

How can I make the bottom div create an 'L' shape as it connects with it's parent div?


Solution

  • You need to set transform-style and make the perspective a bit bigger:

    html {
      font-size: 0.7rem;
    }
    div div {
      top: 5rem;
      background-color: #444;
      transform-origin: top center;
    }
    
    .rotate_x {
      animation-name: rotate_x;
    }
    @keyframes rotate_x {
      0% {
        transform: rotateX( 0deg );
      }
      100% {
        transform: rotateX( 360deg );
      }
    }
    
    .rotate_y {
      animation-name: rotate_y;
      transform-style:preserve-3d; /* HERE */
    }
    @keyframes rotate_y {
      0% {
        transform: rotateY( 0deg );
      }
      100% {
        transform: rotateY( 180deg );
      }
    }
    <style>
      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        animation-duration: 5s;
        animation-iteration-count: infinite;
        animation-timing-function: linear;
      }
      html, body {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100%;
        width: 100%;
      }
      body {
        perspective: 15rem;
      }
      div {
        position: relative;
        top: -1.5rem;
        width: 5rem;
        height: 5rem;
        background-color: #222;
        border-radius: 0.5rem;
      }
    </style>
    
    <div class='rotate_y'>
      <div class='rotate_x'>
      </div>
    </div>