Search code examples
csscss-transformsperspective

perspective and translateZ moves diagonally


Reference this link: https://developer.mozilla.org/en-US/docs/Web/CSS/perspective

Perspective must be set to move a child element along the z-axis. The link above show examples of different perspective values, all of which set the z-axis in a diagonal direction.

If I'm looking directly at the face of a 3D cube and I move it backwards (along the z-axis) it would look like it's getting smaller (moving away from me), not moving diagonally. So why does CSS have a diagonal z-axis by default? Is there a way to use perspective and translateZ with a z-axis that moves exactly away from the user?

Some code I've been testing this with:

.wrapper {
  position: relative;
  height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
  perspective: 1px;
  transform-style: preserve-3d;
}

.cube {
  transform-origin: 50% 50%;
  transform: translateZ(-1px);
}

<div class="wrapper">
  <div class="cube"></div>
</div>

Solution

  • It's all a matter of perspective-origin that define how the changes should be visible to us.

    If you read the same link you will notice this:

    The vanishing point is by default placed at the center of the element, but its position can be changed using the perspective-origin property.

    Here is some example where you can better understand:

    .wrapper {
      position: relative;
      height: 100px;
      width: 100px;
      border: 1px solid;
      perspective: 10px;
      transform-style: preserve-3d;
    }
    
    .cube {
      width: 100%;
      height: 100%;
      background: red;
      animation: change 2s linear infinite alternate;
    }
    
    @keyframes change {
      to {
        transform: translateZ(-10px);
      }
    }
    moving from the center
    <div class="wrapper">
      <div class="cube"></div>
    </div>
    moving from the left
    <div class="wrapper" style="perspective-origin:left">
      <div class="cube"></div>
    </div>
    moving from a custom point
    <div class="wrapper" style="perspective-origin:20% 80%">
      <div class="cube"></div>
    </div>

    You need to also pay attention when you deal with default block element having width:100% as the position will consider the parent element no the child one.

    Remove width and see the difference:

    .wrapper {
      position: relative;
      border: 1px solid;
      perspective: 10px;
      transform-style: preserve-3d;
    }
    
    .cube {
      width: 100px;
      height: 100px;
      background: red;
      animation: change 2s linear infinite alternate;
    }
    
    @keyframes change {
      to {
        transform: translateZ(-10px);
      }
    }
    moving from the center
    <div class="wrapper">
      <div class="cube"></div>
    </div>
    moving from the left
    <div class="wrapper" style="perspective-origin:left">
      <div class="cube"></div>
    </div>
    moving from a custom point
    <div class="wrapper" style="perspective-origin:20% 80%">
      <div class="cube"></div>
    </div>


    In the above codes, the parent container is controlling the perspective. You can move this to the child element like this:

    .wrapper {
      position: relative;
      border: 1px solid;
    }
    
    .cube {
      width: 100px;
      height: 100px;
      background: red;
      animation: change 2s linear infinite alternate;
    }
    
    @keyframes change {
      to {
        transform: perspective(10px) translateZ(-10px);
      }
    }
    moving from the center
    <div class="wrapper">
      <div class="cube"></div>
    </div>
    moving from the left
    <div class="wrapper" >
      <div class="cube" style="transform-origin:left"></div>
    </div>
    moving from a custom point
    <div class="wrapper" >
      <div class="cube" style="transform-origin:20% 80%"></div>
    </div>

    As you can see, we control the origin using transform-origin because we are using perspective a transform-function and no more as a property.

    Change perspective-origin and nothing will happen

    .wrapper {
      position: relative;
      border: 1px solid;
    }
    
    .cube {
      width: 100px;
      height: 100px;
      background: red;
      animation: change 2s linear infinite alternate;
    }
    
    @keyframes change {
      to {
        transform: perspective(10px) translateZ(-10px);
      }
    }
    moving from the center
    <div class="wrapper">
      <div class="cube"></div>
    </div>
    moving from the left
    <div class="wrapper" style="perspective-origin:left">
      <div class="cube" style="perspective-origin:left"></div>
    </div>
    moving from a custom point
    <div class="wrapper" style="perspective-origin:20% 80%">
      <div class="cube" style="perspective-origin:20% 80%"></div>
    </div>