Incorrect stack with box-shadow and transform

2019-06-22 03:34发布

I have created a shape which represents a page with a shadow that gets bigger towards the bottom.

body {
  background: #dddddd;
}
div {
  background: white;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  padding: 10px;
}
div:before,
div:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: rotate(1.5deg);
}
div:after {
  transform: rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div></div>

I need this to be rotated but when i try to add transform: rotate(10deg), the box-shadow illusion gets ruined and goes on top of the parent layer.

body {
  background: #dddddd;
}
div {
  background: white;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  padding: 10px;
  transform: rotate(10deg);
}
div:before,
div:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: rotate(1.5deg);
}
div:after {
  transform: rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div></div>

I have found this question: Which CSS properties create a stacking context? but there doesn't seem to be a proposed solution for my requirement.

Would there be any good solutions which would work in my case. I do not mind if they are SVG, filter, canvas or any thing else as long as it is supported reasonably well.

3条回答
放荡不羁爱自由
2楼-- · 2019-06-22 03:39

When you go into the transforms world, the transforms themselves will solve some of the problems that they create. You can solve this with the 3d equivalent of z-index, the Z coordinate

Unfortunately, this won't work in IE (I believe until they support preserve 3d)

body {
  background: #dddddd;
}
div {
  background: white;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  padding: 10px;
  transform: translateZ(1px) rotate(10deg);
  transform-style: preserve-3d;
}
div:before,
div:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: translateZ(-1px) rotate(1.5deg);
}
div:after {
  transform: translateZ(-1px) rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div></div>

查看更多
三岁会撩人
3楼-- · 2019-06-22 03:57

Note: This answer does not describe how to fix the stacking context problem that is seen in your approach. This just provides a couple of alternate approaches that could be used to achieve a similar effect. Advantage of these approaches is that they should work in IE10+ and does not require any extra elements.

I would still recommend vals' answer if IE support is not mandatory.

Method 1: Perspective Transform

This is almost similar to the one that you had used except that it uses a single pseudo-element rotated with perspective to produce the shadows. Since only one pseudo-element is utilized, the other pseudo can be used to add a white foreground above the shadows.

body {
  background: #dddddd;
}
div {
  position: relative;
  height: 300px;
  width: 300px;
  padding: 10px;
  margin: 40px auto;
  transform: rotate(10deg);
}
div:before,
div:after {
  position: absolute;
  content: '';
  top: 0px;
}
div:before {
  height: 100%;
  width: 100%;
  left: 0px;
  background: white;
}
div:after {
  height: 98%;
  width: 97%;
  left: 1.5%;
  transform-origin: bottom;
  transform: perspective(125px) rotateX(1deg);
  box-shadow: 10px 0px 10px rgba(0, 0, 0, .5), -10px 0px 10px rgba(0, 0, 0, .5);
  z-index: -1;
}
<div></div>


Method 2: Linear Gradients

We can use linear-gradient background images and position them appropriately to produce an effect similar to the one produced by the box-shadows. But as you can see in the output, it doesn't quite match up to a shadow because the blurred areas are not the same.

Here, we make use of the following:

  • One small angled linear gradient image (to top left) to produce the shadow on the left side of the box.
  • Another small angled linear gradient image (to top right) to produce the shadow on the right side of the box.
  • A large linear-gradient image for the white colored area (which is almost a solid color). A gradient is used here instead of a solid color because the size of a gradient image can be controlled.

body {
  background: #dddddd;
}
div {
  margin: 40px auto;
  height: 300px;
  width: 300px;
  transform: rotate(10deg);
  backface-visibility: hidden;
  background: linear-gradient(to right, transparent 0.1%, white 0.1%), linear-gradient(to top left, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%), linear-gradient(to top right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%);
  background-size: 280px 100%, 10px 97%, 10px 97%;
  background-position: 10px 0px, left top, right top;
  background-repeat: no-repeat;
  background-origin: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div></div>

The bottom of the gradient still doesn't get the blur that is seen in the box-shadow output. If needed, this can be achieved to some extent by adding even more gradients like in the below snippet.

body {
  background: #dddddd;
}
div {
  margin: 40px auto;
  height: 300px;
  width: 300px;
  transform: rotate(10deg);
  backface-visibility: hidden;
  background: linear-gradient(to right, transparent 0.1%, white 0.1%), linear-gradient(to top left, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%), linear-gradient(to top right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%), linear-gradient(to bottom left, rgba(0, 0, 0, 0), rgba(0, 0, 0, .3) 5%, transparent 60%), linear-gradient(to bottom right, rgba(0, 0, 0, 0), rgba(0, 0, 0, .3) 5%, transparent 70%);
  background-size: 280px 100%, 10px 97%, 10px 97%, 10px 2.5%, 10px 2.5%;
  background-position: 10px 0px, left top, right top, left 99.25%, right 99.25%;
  background-repeat: no-repeat;
  background-origin: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div></div>

查看更多
Viruses.
4楼-- · 2019-06-22 03:59

If you use another div it fixes the problem that you are experiencing, so that the background colour is on the inner div and the rotate is on the outer div.

Else you might need to use another method to get the same result.

body {
  background: #dddddd;
}
.two{
background: white;
height: 300px;
width: 300px;
padding: 10px;
}
div.one {
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  
  transform: rotate(10deg);
}
div.one:before,
div.one:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: rotate(1.5deg);
}
div.one:after {
  transform: rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div class="one">
<div class="two">
</div>
</div>

查看更多
登录 后发表回答