user2009300
user2009300

Reputation: 57

CSS 3D transform intersection glitch in Chrome / Windows

I've been trying to do some basic work with CSS 3D transforms on HTML elements - however I'm running into a rather extreme glitch in Chrome when using Windows. This issue also occurs on Chrome on Linux, but doesn't seem to happen on Chrome on Safari (tested using AMD, Nvidia and Intel GPU acceleration) :

Chrome 3D CSS Glitch

When multiple 3D transform elements intersect during a keyframed animation, particularly when their parent element is also 3D transformed, it causes visual artifacting that causes both the drawn elements to be displayed incorrectly, which can also paint over the UI for the browser altogether.

* {
  backface-visibility: hidden;
}

body {
  background-color: #333;
}

#stage {
  transform-style: preserve-3d;
}

.box {
  width: 200px;
  height: 200px;
  position: relative;
  transform-style: preserve-3d;
}

.box_one {
  border: 1px solid blue;
  animation: rotate_x 10s infinite;
}

.box_two {
  border: 1px solid red;
  animation: rotate_y 10s infinite;
}

.side {
  width: 200px;
  height: 200px;
  position: absolute;
  border: 1px solid black;
  top: 0;
  left: 0;
}

.side_1 {
  transform: rotateX(90deg) translateZ(100px);
}

.side_2 {
  transform: rotateX(90deg) translateZ(-100px);
}

.side_3 {
  transform: rotateY(90deg) translateZ(100px);
}

.side_4 {
  transform: rotateY(90deg) translateZ(-100px);
}

.side_5 {
  transform: rotateZ(90deg) translateZ(100px);
}

.side_6 {
  transform: rotateZ(90deg) translateZ(-100px);
}

@keyframes rotate_x {
  0% {
    transform: translateX(100px) rotateX(0deg);
  }
  50% {
    transform: translateX(100px) rotateX(360deg);
  }
  100% {
    transform: translateX(100px) rotateX(-0deg);
  }
}

@keyframes rotate_y {
  0% {
    transform: rotateY(0deg);
  }
  50% {
    transform: rotateY(360deg);
  }
  100% {
    transform: rotateY(-0deg);
  }
}

.top {
  transform: rotateX(-90deg);
}
<body>
  <div id="stage" class="top">
    <div class="box box_one">
      <div class="side side_1">

      </div>
      <div class="side side_2">

      </div>
      <div class="side side_3">

      </div>
      <div class="side side_4">

      </div>
      <div class="side side_5">

      </div>
      <div class="side side_6">

      </div>
    </div>
    <div class="box box_two">
      <div class="side side_1">

      </div>
      <div class="side side_2">

      </div>
      <div class="side side_3">

      </div>
      <div class="side side_4">

      </div>
      <div class="side side_5">

      </div>
      <div class="side side_6">

      </div>
    </div>

  </div>

  </div>


</body>

Does anyone have a way of fixing this glitch, or a workaround to avoid it from happening? I've been trying to avoid overlapping where possible, but this still seems to occur with moderately complex transforms and scenes.

Upvotes: 1

Views: 597

Answers (1)

Lars
Lars

Reputation: 3628

I wouldn't know a definite fix for this.
Note that it's not actually the overlapping elements that cause the botched rendering.
It seems to that the parent also having a transform:rotate that is the the actual culprit.
IE. You could remove the first box, and still see the rendering artifacts.

It does seem that the effective width of parent pane has a great effect on this. When you rotateY(90deg) the .top div, the rendered height becomes 0 pixels. Doing a slightly less rotation ie 89deg seems to greatly reduce the artifact.

I'm not sure about your use case, you might get away with applying a overflow:hidden; in the right place. to contain the render artifacts in a acceptable manner.

i've applied a background to .top to make show what the actual size is. Also, i've only applied the backface-visibility:hidden to the .box

$(".no").click(function() {
  $(".top").toggleClass('no-rotate').removeClass('tiny-rotate')
})

$(".tiny").click(function() {
  $(".top").toggleClass('tiny-rotate').removeClass('no-rotate')
})
.box >* {
  backface-visibility: hidden;
}

body {
  background-color: #333;
}

#stage {
  transform-style: preserve-3d;
  transform: rotateX(-180deg)
}

#stage * {
  transform-style: preserve-3d;
}

.box {
  width: 200px;
  height: 200px;
  position: relative;
  transform-style: preserve-3d;
}

.box_one {
  border: 1px solid blue;
  animation: rotate_x 10s infinite;
}

.box_two {
  border: 1px solid red;
  animation: rotate_y 10s infinite;
}

.side {
  width: 200px;
  height: 200px;
  position: absolute;
  border: 1px solid black;
  top: 0;
  left: 0;
}

.side_1 {
  transform: rotateX(90deg) translateZ(100px);
}

.side_2 {
  transform: rotateX(90deg) translateZ(-100px);
}

.side_3 {
  transform: rotateY(90deg) translateZ(100px);
}

.side_4 {
  transform: rotateY(90deg) translateZ(-100px);
}

.side_5 {
  transform: rotateZ(90deg) translateZ(100px);
}

.side_6 {
  transform: rotateZ(90deg) translateZ(-100px);
}

@keyframes rotate_x {
  0% {
    transform: translateX(100px) rotateX(0deg);
  }
  50% {
    transform: translateX(100px) rotateX(360deg);
  }
  100% {
    transform: translateX(100px) rotateX(-0deg);
  }
}

@keyframes rotate_y {
  0% {
    transform: rotateY(0deg);
  }
  50% {
    transform: rotateY(360deg);
  }
  100% {
    transform: rotateY(-0deg);
  }
}

.top {
  transform: rotateX(-90deg);
  background-color: OldLace;
  transition: transform 2s;
}

.top.no-rotate {
  transform: rotateX(0deg);
}

.top.tiny-rotate {
  transform: rotateX(-89deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<body>
  <button class="no">toggle no rotate</button>
  <button class="tiny">toggle tiny rotate</button>
  <div id="stage">
    <div class="top no-rotate">
      <div class="box box_one">
        <div class="side side_1"> </div>
        <div class="side side_2"> </div>
        <div class="side side_3"> </div>
        <div class="side side_4"> </div>
        <div class="side side_5"> </div>
        <div class="side side_6"> </div>
      </div>
      <div class="box box_two">
        <div class="side side_1"> </div>
        <div class="side side_2"> </div>
        <div class="side side_3"> </div>
        <div class="side side_4"> </div>
        <div class="side side_5"> </div>
        <div class="side side_6"> </div>
      </div>
    </div>
  </div>


</body>

Upvotes: 1

Related Questions