Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Border radius and overflow aren't respected when animation is added on a different element

I got a weird behavior when working with transform in CSS3.

Here is my code:

*, *:before, *:after {
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
}

.btn_exchange a {
    position: relative;
    text-decoration: none;
    display: block;
    width: 150px;
    float: left;
    color: #ffffff;
    font-size: 14px;
    background: #0ea2d3;
    box-shadow: 0 3px 0 0 rgba(7, 154, 190, 0.75);
    text-align: center;
    font-weight: bold;
}

@keyframes pulse {
    0% {
        transform: scale(1);
    }
    50% {
        transform: scale(1.05);
    }
    100% {
        transform: scale(1);
    }
}

@-webkit-keyframes pulse {
    0% {
        transform: scale(1);
    }
    50% {
        transform: scale(1.05);
    }
    100% {
        transform: scale(1);
    }
}

a.abc {
    z-index: 0;
    -webkit-animation: pulse 1s linear 0s infinite alternate;
    animation: pulse 1s linear infinite;
}

#box_avatar {
    position: relative;
    margin: 0 auto;
    -webkit-border-radius: 62.5px;
    -moz-border-radius: 62.5px;
    -ms-border-radius: 62.5px;
    -o-border-radius: 62.5px;
    border-radius: 62.5px;
    width: 125px;
    height: 125px;
    border: 2px solid #ccc;
    display: block;
    overflow: hidden;
}

#img_avatar {
    width: 125px;
    height: 125px;
    cursor: pointer;
    border-radius: 62.5px;
}

#bg_gray {
    background: #4c4747;
    width: 100%;
    position: absolute;
    bottom: 0;
    padding: 8px 0 10px 0;
    opacity: 0.8;
}

#bg_gray img {
    display: block;
    width: 20px;
    margin: 0 auto;
}
<div class="btn_exchange fl bad">
  <a class="button abc" href="#">Button</a>
</div>

<div id="box_avatar">
  <img id="img_avatar" src="http://i.imgur.com/O29DJOZ.jpg" alt="avatar" />
  <div id="bg_gray">
    <img src="http://i.imgur.com/m5qIRID.png" alt="btn_camera" />
  </div>
</div>

<div class="btn_exchange fl good">
  <a class="button abc" href="#">Button</a>
</div>

The problem happens when I use the .bad div (remove .good div) then the gray background which contains the camera-icon does not lay inside the circle image.

If I remove the animation transform in CSS, the button won't pulse anymore but the gray background will be inside circle image.

Anyone know what it is and how to solve it?

like image 299
omgpwned Avatar asked Dec 29 '15 08:12

omgpwned


1 Answers

This problem is quite similar to the one that I had answered earlier. The reason why the gray box with the camera icon is not contained within the circle (not cropped by the circle) is because of how layers are created and accelerated rendering is performed by browsers. You can find more information about it in the answer that I had linked earlier.

Solution:

The solution to your problem would be to move the pulsing buttons to a layer higher by setting a higher z-index value to it. In the below snippet, I have set it to 1 and you can see that it solves the problem.

*, *:before, *:after {
  box-sizing: border-box;
}
.btn_exchange a {
  position: relative;
  display: block;
  float: left;
  width: 150px;
  color: #ffffff;
  font-size: 14px;
  text-decoration: none;
  text-align: center;
  font-weight: bold;
  background: #0ea2d3;
  box-shadow: 0 3px 0 0 rgba(7, 154, 190, 0.75);
}
@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.05);
  }
  100% {
    transform: scale(1);
  }
}
a.abc {
  z-index: 1;
  animation: pulse 1s linear infinite;
}
#box_avatar {
  position: relative;
  display: block;
  width: 125px;
  height: 125px;
  margin: 0 auto;
  border-radius: 62.5px;
  border: 2px solid #ccc;
  overflow: hidden;
}
#img_avatar {
  width: 125px;
  height: 125px;
  border-radius: 62.5px;
  cursor: pointer;
}
#bg_gray {
  position: absolute;
  width: 100%;
  bottom: 0;
  padding: 8px 0 10px 0;
  background: #4c4747;
  opacity: 0.8;
}
#bg_gray img {
  display: block;
  width: 20px;
  margin: 0 auto;
}
<div class="btn_exchange fl bad">
  <a class="button abc" href="#">Button</a>
</div>

<div id="box_avatar">
  <img id="img_avatar" src="http://i.imgur.com/O29DJOZ.jpg" alt="avatar" />
  <div id="bg_gray">
    <img src="http://i.imgur.com/m5qIRID.png" alt="btn_camera" />
  </div>
</div>

<div class="btn_exchange fl good">
  <a class="button abc" href="#">Button</a>
</div>

Applying a z-index for the #box_avatar would also solve the issue.


Reason:

Rendering and compositing are tricky to explain but the following is what happens when no z-index or z-index: 0 is set to the a.abc:

  • The a.abc gets its own rendering and compositing layer because it has an animated transform and explicit positioning properties.
  • The rendering and compositing layer creation process is explained in this article. The layers can be seen by enabling the "Show Paint Rects" and "Show composited layer borders" in Dev tools.
  • The #box_avatar and #bg_gray also get their own compositing layer. The #box_avatar seems to be getting a separate layer because it has a previous sibling with equal or lower z-index.
  • I am not able to exactly pinpoint why the #bg_gray gets a separate layer but it seems to mostly be because of how z-index: auto creates a separate stacking context. (Source - W3C Spec)

When distinct layers are created for #box_avatar, #bg_gray and they are placed one on top of the other, the #bg_gray isn't exactly inside the #box_avatar and so doesn't respect the overflow and border-radius. It is kind of like placing two layers of paper one on top of the other.


Detailed Explanation of Solution:

When z-index: 1 is assigned on a.abc element:

  • The a.abc gets its own rendering and compositing layer because it has an animated transform.
  • The container of a.abc also gets its layer because it has a child which has a compositing layer.
  • Neither #box_avatar nor #bg_gray get a separate compositing layer because they don't meet any of the criteria defined to get a compositing layer.

When z-index: 0 is assigned on a.abc and z-index: 1 is assigned on #box_avatar:

  • The a.abc gets its own rendering and compositing layer because it has an animated transform
  • The container of a.abc also gets its layer because it has a child which has a compositing layer
  • The #box_avatar gets its own rendering and compositing layer because it has a sibling (a.abc element's container) which has a compositing layer with lower z-index.
  • The #bg_gray becomes a part of the #box_avatar element's layer and gets no separate layer.

In both the above cases, because the #box_avatar and #bg_gray don't get separate layers, they are kind of painted together and so the #bg_gray knows where to get cropped.

like image 115
Harry Avatar answered Nov 03 '22 04:11

Harry