Search code examples
javascripthtmlcsscss-transforms

3d transform: DOM order taking priority over z-transform


I'm trying to make a rotating cube, however I'm finding that the sides of the cube are being displayed depending on their order in the DOM rather than their 3d position.

  • Is there something I'm missing to get this to use 3d position over DOM order?

I'm partially following this tutorial: https://3dtransforms.desandro.com/cube

  • The code snippet has to be viewed in fullpage mode in order to see the control for 3d transitions
  • After checking browserstack, the issue seems to only be happening in Chrome

document.addEventListener('DOMContentLoaded', function(){
    let control = document.querySelector("#control")
    let cube = document.querySelector('.cube')
    let re = /show\-.*/
    control.addEventListener('change', (evt)=>{
        cube.classList.forEach((value, key, parent)=>{
            if (re.test(value)){
                cube.classList.remove(value)
            }
        })
        cube.classList.add(`show-${control.value}`)
    })
})
body{
    margin: 0;
    width: 100vw;
    height: 100vh;
    
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    flex-direction: column;
}
.scene {
    width: 200px;
    height: 200px;
    perspective: 600px;
  }
  
  .cube {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
  }
  
  .cube__face {
    position: absolute;
    width: 200px;
    height: 200px;
  }

.cube__face--front  { transform: rotateY(  0deg) translateZ(100px); }
.cube__face--right  { transform: rotateY( 90deg) translateZ(100px); }
.cube__face--back   { transform: rotateY(180deg) translateZ(100px); }
.cube__face--left   { transform: rotateY(-90deg) translateZ(100px); }
.cube__face--top    { transform: rotateX( 90deg) translateZ(100px); }
.cube__face--bottom { transform: rotateX(-90deg) translateZ(100px); }

.cube.show-front  { transform: translateZ(-100px) rotateY(   0deg); }
.cube.show-right  { transform: translateZ(-100px) rotateY( -90deg); }
.cube.show-back   { transform: translateZ(-100px) rotateY(-180deg); }
.cube.show-left   { transform: translateZ(-100px) rotateY(  90deg); }
.cube.show-top    { transform: translateZ(-100px) rotateX( -90deg); }
.cube.show-bottom { transform: translateZ(-100px) rotateX(  90deg); }

.cube { transition: transform 3s; }
<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <link rel="stylesheet" href="style.css">
        <script src="main.js"></script>
    </head>

    <body>
        <div class="scene">
            <div class="cube">
                <img class="cube__face cube__face--back" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                <img class="cube__face cube__face--right" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                <img class="cube__face cube__face--left" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                <img class="cube__face cube__face--top" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                <img class="cube__face cube__face--bottom" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                <img class="cube__face cube__face--front" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
            </div>
        </div>
        <div class="controls">
            <select name="control" id="control" value="front">
                <option value="front">front</option>
                <option value="back">back</option>
                <option value="right">right</option>
                <option value="left">left</option>
                <option value="top">top</option>
                <option value="bottom">bottom</option>
            </select>
        </div>
    </body>

</html>


Solution

  • Adding background to the images seems to fix the isssue:

    document.addEventListener('DOMContentLoaded', function(){
        let control = document.querySelector("#control")
        let cube = document.querySelector('.cube')
        let re = /show\-.*/
        control.addEventListener('change', (evt)=>{
            cube.classList.forEach((value, key, parent)=>{
                if (re.test(value)){
                    cube.classList.remove(value)
                }
            })
            cube.classList.add(`show-${control.value}`)
        })
    })
    body{
        margin: 0;
        width: 100vw;
        height: 100vh;
        
        display: flex;
        justify-content: space-evenly;
        align-items: center;
        flex-direction: column;
    }
    .scene {
        width: 200px;
        height: 200px;
        perspective: 600px;
      }
      
      .cube {
        width: 100%;
        height: 100%;
        position: relative;
        transform-style: preserve-3d;
      }
      
      .cube__face {
        position: absolute;
        width: 200px;
        height: 200px;
      }
    
    .cube__face--front  { transform: rotateY(  0deg) translateZ(100px); }
    .cube__face--right  { transform: rotateY( 90deg) translateZ(100px); }
    .cube__face--back   { transform: rotateY(180deg) translateZ(100px); }
    .cube__face--left   { transform: rotateY(-90deg) translateZ(100px); }
    .cube__face--top    { transform: rotateX( 90deg) translateZ(100px); }
    .cube__face--bottom { transform: rotateX(-90deg) translateZ(100px); }
    
    .cube.show-front  { transform: translateZ(-100px) rotateY(   0deg); }
    .cube.show-right  { transform: translateZ(-100px) rotateY( -90deg); }
    .cube.show-back   { transform: translateZ(-100px) rotateY(-180deg); }
    .cube.show-left   { transform: translateZ(-100px) rotateY(  90deg); }
    .cube.show-top    { transform: translateZ(-100px) rotateX( -90deg); }
    .cube.show-bottom { transform: translateZ(-100px) rotateX(  90deg); }
    
    .cube { transition: transform 3s; }
    
    img {
      background:#ffde39;
    }
    <!DOCTYPE html>
    <html lang="en">
    
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <link rel="stylesheet" href="style.css">
            <script src="main.js"></script>
        </head>
    
        <body>
            <div class="scene">
                <div class="cube">
                    <img class="cube__face cube__face--back" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                    <img class="cube__face cube__face--right" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                    <img class="cube__face cube__face--left" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                    <img class="cube__face cube__face--top" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                    <img class="cube__face cube__face--bottom" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                    <img class="cube__face cube__face--front" src="https://media.giphy.com/media/l4KhKRcaYb43LVqq4/source.gif">
                </div>
            </div>
            <div class="controls">
                <select name="control" id="control" value="front">
                    <option value="front">front</option>
                    <option value="back">back</option>
                    <option value="right">right</option>
                    <option value="left">left</option>
                    <option value="top">top</option>
                    <option value="bottom">bottom</option>
                </select>
            </div>
        </body>
    
    </html>