Search code examples
htmlcsscss-animationscss-transforms

Animating faces on a CSS isometric cube


I'm trying to animate the faces of an isometric cube I've created using CSS transforms to create an 'unpacking/unfolding' effect.

I want the lid of the cube to rotate upwards but at the moment it floats off rather than rotates from the edge. It starts and ends in the right places. I've tried changing the transform-origin property but it doesn't make a difference. Here's my code so far:

https://jsfiddle.net/wrgt1/5yrLjnw3/38/

html body {
  position: relative;
  height: 100vh;
  width: 100vw;
  background-color: #C4C5C4;
}

.front,
.back {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
}

.front {
  z-index: 99;
}

.cube {
  transform-style: preserve-3d;
  transform: rotateX(-35deg) rotateY(45deg);
}

.face {
  position: absolute;
  width: 30vh;
  height: 30vh;
  background: white;
  border: solid 1px blue;
}

.top {
  transform-origin: 100% 100%;
  transform: rotateX(90deg) rotateY(0deg) translate3d(15vh, 0, 15vh);
  animation: rotatelid 5s linear infinite alternate;
}

.frontleft {
  transform: rotateY(-90deg) translate3d(0, 0, 15vh);
}

.frontright {
  transform: translate3d(0, 0, 15vh);
}

.backleft {
  transform: translate3d(0, 0, -15vh);
  background: lightgrey;
}

.backright {
  transform: rotateY(-90deg) translate3d(0, 0, -15vh);
}

@keyframes rotatelid {
  from {
    transform: rotateX(90deg) rotateY(0deg) translate3d(15vh, 0, 15vh);
  }

  to {
    transform: rotateX(90deg) rotateY(90deg) translate3d(-15vh, 0vh, 15vh);
  }
}
<div class='front'>
  <div class='cube'>
    <div class='face top'></div>
    <div class='face frontleft'></div>
    <div class='face frontright'></div>
  </div>
</div>

<div class='back'>
  <div class='cube'>
    <div class='face backleft'></div>
    <div class='face backright'></div>
  </div>
</div>

Does anyone know how to solve this problem, or if there's a better way to create simple animations on the web (possibly using SVGs?).


Solution

  • Update the order of your transformation to first translate the element then rotate it. Pay attention to the translation because it's no more the same when added first.

    html body {
      position: relative;
      height: 100vh;
      margin:0;
      background-color: #C4C5C4;
    }
    
    .front,
    .back {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      position: absolute;
    }
    
    .front {
      z-index: 99;
    }
    
    .cube {
      transform-style: preserve-3d;
      transform: rotateX(-35deg) rotateY(45deg);
    }
    
    .face {
      position: absolute;
      width: 30vh;
      height: 30vh;
      background: white;
      border: solid 1px blue;
      box-sizing:border-box;
    }
    
    .top {
      transform-origin: 100% 100%;
      transform: translate3d(15vh, -15vh, 0vh) rotateX(90deg) rotateY(0deg);
      animation: rotatelid 5s linear infinite alternate;
    }
    
    .frontleft {
      transform: rotateY(-90deg) translate3d(0, 0, 15vh);
    }
    
    .frontright {
      transform: translate3d(0, 0, 15vh);
    }
    
    .backleft {
      transform: translate3d(0, 0, -15vh);
      background: lightgrey;
    }
    
    .backright {
      transform: rotateY(-90deg) translate3d(0, 0, -15vh);
    }
    
    @keyframes rotatelid {
      from {
        transform:translate3d(15vh, -15vh, 0vh) rotateX(90deg) rotateY(0deg);
      }
    
      to {
        transform:translate3d(15vh, -15vh, 0vh) rotateX(90deg) rotateY(90deg);
      }
    }
    <div class='front'>
      <div class='cube'>
        <div class='face top'></div>
        <div class='face frontleft'></div>
        <div class='face frontright'></div>
      </div>
    </div>
    
    <div class='back'>
      <div class='cube'>
        <div class='face backleft'></div>
        <div class='face backright'></div>
      </div>
    </div>