cssgoogle-chromecss-transformswill-change

Chrome CSS bug: "translate3d" inside a rotated element does not obey "overflow: hidden"


Take a look at this CodePen to see what I mean:

.perspective-container {
  margin: 50px 0;
  perspective: 1000px;
  perspective-origin: 0 50%;
}
.card {
  border: 10px solid blue;
  width: 300px;
  height: 300px;
  overflow: hidden;
  transform: rotateY(-45deg);
}
.card-inner {
  width: 300px;
  height: 300px;
  overflow: hidden;
}
.scroller {
  transform: translate(0, -100px);
}
.scroller-3d {
  transform: translate3d(0, -100px, 0);
}
.will-change {
  will-change: transform;
}
<h1>Incorrect (uses will-change):</h1>
<div class="perspective-container">
  <div class="card"><div class="card-inner">
    <div class="scroll-container">
      <div class="scroller will-change">
        <img src="https://images2.pixlis.com/background-image-horizontal-lines-and-stripes-seamless-tileable-grey-black-22hnju.png" />
      </div>
    </div>
  </div></div>
</div>
<h1>Incorrect (uses translate3d):</h1>
<div class="perspective-container">
  <div class="card">
    <div class="scroll-container">
      <div class="scroller-3d">
        <img src="https://images2.pixlis.com/background-image-horizontal-lines-and-stripes-seamless-tileable-grey-black-22hnju.png" />
      </div>
    </div>
  </div>
</div>
<h1>Correct (uses neither translate3d or will-change):</h1>
<div class="perspective-container">
  <div class="card">
    <div class="scroll-container">
      <div class="scroller">
        <img src="https://images2.pixlis.com/background-image-horizontal-lines-and-stripes-seamless-tileable-grey-black-22hnju.png" />
      </div>
    </div>
  </div>
</div>

I have a rotated element with perspective applied, inside of which I have a 'scrolling' div which is transformed vertically (imagine a magazine page-turn experience with scrolling divs inside each page). I added will-change: transform to the inner div and that broke the outer container overflow: hidden. This seems to be a bug with the Chrome compositor.

Does anyone know of any workarounds? I would like to keep the will-change attribute because it significantly speeds-up animations on mobile Chrome.

Edit: It looks like this is not specific to the will-change property but any property that makes the inner div its own compositing layer. When I remove the will-change property but change the transform to a translate3d that similarly improves performance and exhibits the bug. The only class on the inner div that allows the rotated parent to correctly render overflow: hidden is the one that adds the 2D transform.


Solution

  • Try something like this CodePen:

    .container {
      margin: 75px 0;
    }
    
    .card {
      border: 10px solid blue;
      width: 300px;
      height: 300px;
      overflow: hidden;
      -webkit-transform: perspective(1000px) rotateY(-45deg);
      transform: perspective(1000px) rotateY(-45deg);
      -webkit-transform-origin: 0 50%;
      transform-origin: 0 50%;
    }
    
    .scroller {
      -webkit-transform: translate(0, -100px);
      transform: translate(0, -100px);
    }
    
    .scroller-3d {
      -webkit-transform: translate3d(0, -100px, 0);
      transform: translate3d(0, -100px, 0);
    }
    
    .will-change {
      will-change: transform;
    }
    <h1>Uses will-change:</h1>
    <div class="container">
      <div class="card">
        <div class="scroll-container">
          <div class="scroller will-change">
            <img src="https://images2.pixlis.com/background-image-horizontal-lines-and-stripes-seamless-tileable-grey-black-22hnju.png" />
          </div>
        </div>
      </div>
    </div>
    
    <h1>Uses translate3d:</h1>
    <div class="container">
      <div class="card">
        <div class="scroll-container">
          <div class="scroller-3d">
            <img src="https://images2.pixlis.com/background-image-horizontal-lines-and-stripes-seamless-tileable-grey-black-22hnju.png" />
          </div>
        </div>
      </div>
    </div>
    
    <h1>Uses neither translate3d or will-change:</h1>
    <div class="container">
      <div class="card">
        <div class="scroll-container">
          <div class="scroller">
            <img src="https://images2.pixlis.com/background-image-horizontal-lines-and-stripes-seamless-tileable-grey-black-22hnju.png" />
          </div>
        </div>
      </div>
    </div>

    You could play with perspective() and transform-origin values.