Transformed child clipped by parent background in

2019-07-25 10:23发布

问题:

I've got this loader, rendering fine in everything, but Safari:

.pageLoader {
  min-height: 100vh;
  min-width: 100vw;
  background-color: white;
  z-index: 2;
  position: fixed;
  opacity: .9;
  top: 0;
  left: 0;
  transition: opacity .6s linear;
  display: flex;
  justify-content: center;
  align-items: center;
}
.pageSpinner {
   border-radius: 5%;
   width: 80px;
   height: 80px;
   background-color: #f50;
   animation: a 1.5s infinite cubic-bezier(.4,0,.2,1);
}
@keyframes a {
  0% { transform: perspective(300px) rotateX(0deg) rotateY(0deg); }
  50% { transform: perspective(300px) rotateX(-180deg) rotateY(0deg); }
  100% { transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); }
}
<div class="pageLoader">
  <div class="pageSpinner"></div>
</div>

Fully prefixed version here:

.pageLoader {
  min-height: 100vh;
  min-width: 100vw;
  background-color: white;
  z-index: 2;
  position: fixed;
  opacity: .9;
  top: 0;
  left: 0;
  -webkit-transition: opacity .6s linear;
  -o-transition: opacity .6s linear;
  -moz-transition: opacity .6s linear;
  transition: opacity .6s linear;
  display: -webkit-box;
  display: -webkit-flex;
  display: -moz-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
     -moz-box-pack: center;
      -ms-flex-pack: center;
          justify-content: center;
  -webkit-box-align: center;
  -webkit-align-items: center;
     -moz-box-align: center;
      -ms-flex-align: center;
          align-items: center;
}
.pageSpinner {
   -webkit-border-radius: 5%;
      -moz-border-radius: 5%;
           border-radius: 5%;
   width: 80px;
   height: 80px;
   background-color: #f50;
   -webkit-animation: a 1.5s infinite cubic-bezier(.4,0,.2,1);
      -moz-animation: a 1.5s infinite cubic-bezier(.4,0,.2,1);
        -o-animation: a 1.5s infinite cubic-bezier(.4,0,.2,1);
           animation: a 1.5s infinite cubic-bezier(.4,0,.2,1);
}
@-webkit-keyframes a {
  0% { -webkit-transform: perspective(300px) rotateX(0deg) rotateY(0deg); transform: perspective(300px) rotateX(0deg) rotateY(0deg); }
  50% { -webkit-transform: perspective(300px) rotateX(-180deg) rotateY(0deg); transform: perspective(300px) rotateX(-180deg) rotateY(0deg); }
  100% { -webkit-transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); }
}
@-moz-keyframes a {
  0% { -moz-transform: perspective(300px) rotateX(0deg) rotateY(0deg); transform: perspective(300px) rotateX(0deg) rotateY(0deg); }
  50% { -moz-transform: perspective(300px) rotateX(-180deg) rotateY(0deg); transform: perspective(300px) rotateX(-180deg) rotateY(0deg); }
  100% { -moz-transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); }
}
@-o-keyframes a {
  0% { transform: perspective(300px) rotateX(0deg) rotateY(0deg); }
  50% { transform: perspective(300px) rotateX(-180deg) rotateY(0deg); }
  100% { transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); }
}
@keyframes a {
  0% { -webkit-transform: perspective(300px) rotateX(0deg) rotateY(0deg); -moz-transform: perspective(300px) rotateX(0deg) rotateY(0deg); transform: perspective(300px) rotateX(0deg) rotateY(0deg); }
  50% { -webkit-transform: perspective(300px) rotateX(-180deg) rotateY(0deg); -moz-transform: perspective(300px) rotateX(-180deg) rotateY(0deg); transform: perspective(300px) rotateX(-180deg) rotateY(0deg); }
  100% { -webkit-transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); -moz-transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); transform: perspective(300px) rotateX(-180deg) rotateY(-180deg); }
}
<div class="pageLoader">
  <div class="pageSpinner"></div>
</div>

Safari, for some reason, refuses to render the part of the perspective that is behind the "center" of the transformation. What I have tried:

  • backface-visibility: hidden|visible|initial;
  • transform-style: preserve-3d; in conjunction with adding translateZ(1000) to the list of transforms
  • perspective-origin-z: (various values, for both parent and child).
  • z-index (various values, for both parent and child with position:relative on child).

Please note I already found a solution: I placed a second child besides .pageSpinner into the parent and moved background-color from parent to that child. And it works: .pageSpinner renders above its sibling, when properly z-indexed. However,

I don't understand why parents' background, when opaque, clips the child. Shouldn't the background render below children no matter what? My current understanding of stacking contexts is that each positioned parent is the stacking context for all its children. And parents' background should render below this stacking context.

Could anyone explain what's different in Safari in this regard? Does it have to do with parent's position:fixed? If it has any importance, the parent is a direct child of the body element in its original context.

Another note: a few properties might be irrelevant for the question at hand, such as opacity on parent and its transitions but since they are also present in the original code (and required in production), I thought I should leave them in here as well, just in case they somehow have an influence on the bug.