Is there any way 开发者_开发技巧to create a css box-shadow in which regardless of the blur value, the shadow only appears on the desired sides?
For example if I want to create a div with shadows on left and right sides and no shadow on the top or bottom. The div is not absolutely positioned and its height is determined by the content.
-- Edit --
@ricebowl: I appreciate your answer. Maybe you can help with creating a complete solution to fix the problems stated in my reply to your solution... My page setup is as follows:
<div id="container">
<div id="header"></div>
<div id="content"></div>
<div id="clearfooter"></div>
</div>
<div id="footer"></div>
And CSS like this:
#container {width:960px; min-height:100%; margin:0px auto -32px auto;
position:relative; padding:0px; background-color:#e6e6e6;
-moz-box-shadow: -3px 0px 5px rgba(0,0,0,.8),
3px 0px 5px rgba(0,0,0,.8);}
#header {height:106px; position:relative;}
#content {position:relative;}
#clearFooter {height:32px; clear:both; display:block; padding:0px; margin:0px;}
#footer {height:32px; padding:0px; position:relative; width:960px; margin:0px
auto 0px auto;}
There is a fourth distance you can define called the spread offset, which moves the shadow in or out on all 4 sides. So if you set that to the negative of the blur distance, this will shift the shadow inwards by the same distance as the blur extends the shadow outwards effectively hiding it. Of course this will also shift the shadow inwards on the side you do want it to appear so you'll need to increase the offset by the blur distance to undo that. i.e.
box-shadow: (horizontal + blur) 0px (blur) (-blur) color;
So in your example:
box-shadow: -8px 0px 5px -5px rgba(0,0,0,.8), 8px 0px 5px -5px rgba(0,0,0,.8);
Update
clip-path
, as of 2020, is supported in all major browsers. (See Solution 1 below).
Original Answer
I have 2 possible solutions that produce exactly the desired effect: a "normal" box shadow on some edges and nothing on other edges. Many of the solutions listed in this and other S.O. questions result in shadows that "dissipate" as they near the edge that is to have no shadow, when really I believe most people are wanting a clean cut-off.
However, both solutions come with caveats.
Solution 1: clip-path (experimental)
If you are willing to use experimental technology with only partial support, you could use the clip-path
property.
In your case you would use clip-path: inset(px px px px);
where the pixel values are calculated from the edge in question (see below).
#container {
box-shadow: 0 0 5px rgba(0,0,0,0.8);
clip-path: inset(0px -5px 0px -5px);
}
This will clip the div in question at:
- 0 pixels from the top
- 5 pixels outside of the right edge (to include the shadow)
- 0 pixels from the bottom
- 5 pixels outside of the left edge (to include the shadow)
Note that no commas are required between pixel values.
Absolute positioning is not required and the size of the div can be flexible.
Solution 2: clip (deprecated)
If:
- you were willing to set
position: absolute
on the div in question - AND you know the dimensions of the div
- OR you don't know the dimensions of the div but you are willing to only remove the top and/or left shadows
...you could use the deprecated clip
property.
You'd need to use clip: rect(px, px, px, px);
where the pixel values are calculated from the top left. I've used it as follows to cut off the top box-shadow but keep the bottom and sides:
#container {
position: absolute;
box-shadow: 0 0 5px rgba(0,0,0,0.8);
width: 100px;
height: 100px;
clip: rect(0px, 105px, 100px, -5px);
}
The above will clip the top and bottom box-shadows while leaving the 5px left and right box-shadows. Note that the size of the div must be known.
If the size of the div is not known this method will only work to clip the top and left shadows using something like clip: rect(0, 3000px, 3000px, 0);
(note the massive value for the right and bottom values to allow the div to be any size).
You can also use clip:rect(0px, 210px, 200px, -10px);
Sadly I was never able to figure out a way to get it to work with a flexible sized box.
I often use this for drop down menues where I only want shadow on sides and bottom. In that case I just set the right and bottom clip values to some high number, such as
clip:rect(0px, 1000px, 1000px, -10px); /* Clip the top of the box-shadow off */
#box{
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.7);
-moz-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.7);
-webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.7);
clip:rect(0px, 210px, 200px, -10px); /* Clip the top and bottom of the box-shadow off */
width:200px;
height: 200px;
position: absolute;
top:50px;
left:50px;
background:#eee;
}
There is, but it's fairly fragile.
Using the following xhtml:
<div id="wrap">
<div id="content">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sodales justo nec mauris aliquam vitae feugiat magna congue. Morbi dignissim volutpat dui id porttitor. Donec auctor feugiat dolor, at varius magna rhoncus sed. Vivamus a odio urna, iaculis dignissim lectus. Integer aliquam felis eu sapien vestibulum ornare. Vivamus nec euismod sapien. Mauris quis eros ligula, sed pulvinar sem. Aenean sodales tempor malesuada. Aliquam erat volutpat. Aenean vel eros velit, et porttitor elit. Phasellus volutpat blandit quam eu fringilla. Integer ornare convallis tincidunt. Suspendisse commodo iaculis est vulputate volutpat. Donec at massa arcu. Sed sit amet commodo mauris. Aliquam erat volutpat. Integer eu augue vel erat euismod volutpat eu vel massa. Curabitur id erat vitae nisi imperdiet scelerisque id ut arcu. Quisque commodo dolor vitae erat imperdiet consectetur.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sodales justo nec mauris aliquam vitae feugiat magna congue. Morbi dignissim volutpat dui id porttitor. Donec auctor feugiat dolor, at varius magna rhoncus sed. Vivamus a odio urna, iaculis dignissim lectus. Integer aliquam felis eu sapien vestibulum ornare. Vivamus nec euismod sapien. Mauris quis eros ligula, sed pulvinar sem. Aenean sodales tempor malesuada. Aliquam erat volutpat. Aenean vel eros velit, et porttitor elit. Phasellus volutpat blandit quam eu fringilla. Integer ornare convallis tincidunt. Suspendisse commodo iaculis est vulputate volutpat. Donec at massa arcu. Sed sit amet commodo mauris. Aliquam erat volutpat. Integer eu augue vel erat euismod volutpat eu vel massa. Curabitur id erat vitae nisi imperdiet scelerisque id ut arcu. Quisque commodo dolor vitae erat imperdiet consectetur.</p>
</div>
</div>
And the following css:
#wrap {width: 70%;
margin: 1em auto;
overflow: hidden;
overflow-x: visible;
}
#content
{width: 90%;
margin: 0 auto;
-moz-box-shadow: 0 0 1em #ccc;
-webkit-box-shadow: 0 0 1em #ccc;
}
#content p
{overflow-y: hidden;
padding: 0.5em 0;
}
(Live demo located here: http://davidrhysthomas.co.uk/so/shadows.html.)
The fragility creeps in if you add margins to the contained elements (especially the <p>
elements, which is why I used padding
instead). But, pretty much, apply the -moz-box-shadow
(and/or -webkit-box-shadow
) to the #content
div, and use the #wrap
div to clip the shadow, using overflow-y: hidden;
this, of course, makes it even more fragile due to the number of browsers that respect overflow-y
.
On the other hand, the browsers that interpret the box-shadow
more or less certainly deal appropriately with overflow-y
.
The way to do this is to put the box with the shadow under a div that has overflow set to 'hidden'. For example, to create a shadow around a box that only appears on the left, top and right sides:
CSS:
#container {
height: 101px;
overflow: hidden;
padding: 5px 5px 0;
width: 105px;
}
#shadow-box {
border:1px solid #aaa;
width:100px;
height:100px;
box-shadow:0 0 4px 1px #666;
}
HTML:
<div id="container">
<div id="shadow-box"></div>
<div>
Display:
You can adjust the #container padding and size to clip appropriate for your needs. In this example, I'm clipping the bottom border of the #shadow-box.
As I stated in a related question of mine the solution to this problem is either very obscure or is not possible with the current technology. Its really too bad there is no way of accomplishing this as it is a common theme in web design.
I resorted to using a png shadow as it seems to be the only sane solution.
Thanks for all of your suggestions.
You can prevent a shadow/blur on an edge if you set the z-index for the target element and it's neighboring element. On the edge that you don't want a shadow/blur, place the neighboring element on a higher plane through the z-index property. For example, if you have a side navigation with a box shadow "box-shadow: 6px 0px 10px #bdbdbd", the box-shadow property will cause some bleeding on the top edge (onto the header). You can prevent the blur from the box-shadow property to bleed onto the header element by setting a higher value for the z-index property on the header than the z-index for the nav element. You will need to set the position for both the header and nav elements to "relative" otherwise the z-index will not work (z-index does not work with the default position "static").
I randomly selected the value 10 for the header's z-index and 9 for the nav element's z-index. This places the header on top of the nav element and you will not see a shadow/blur on that edge.
#header {
background-color: #1c2a48;
display: flex;
position: relative;
z-index: 10;
width: 100%;
}
#nav {
display: flex;
float: left;
position: relative;
z-index: 9;
height: calc(100vh - 44.6px) !important;
width: 187px;
border-right: .5px solid rgba(180,180,180,0.7);
background-color: #fafafa;
box-shadow: 6px 0px 10px #bdbdbd;
}
I think there is always likely to be a little bleed-through to adjacent sides that will become more obvious with higher blur values. You can offset the perceived bleed-through by using a low blur and a higher offset on the sides you want the shadow to appear on. For example, this would create a noticeable shadow on the sides and a nearly invisible one on the top and bottom:
box-shadow: #888 3px 0px 2px, #888 -3px 0px 2px;
Michaels spread-offset
based solution is pretty good. Problem with that is that the box shadow doesn't run to the edge which probably is exactly what most people want the shadow to look like.
Below is my clip-path
based approach wich not only let you cut the shadow hard but also allows for better control on which sides the shadow should appear. Here a comparison between normal, spread-offset and clip-path:
.container {
padding: 65px 0px;
text-align: center;
}
.box {
display: inline-block;
width: 50px;
height: 50px;
text-align: center;
line-height: 50px;
background: #0f0;
margin: 15px;
}
/* default box shadow */
.box1 {
box-shadow: 0px 0px 10px 5px #000;
}
/* single side with spread-offset */
.box2 {
box-shadow: 0px 10px 5px -5px rgba(0,0,0,.8);
}
/* single side with clip-path */
.box3 {
box-shadow: 0px 0px 10px 5px #000;
clip-path: polygon(0 0, 100% 0, 100% 300%, 0 300%);
}
<div class="container">
<div class="box box1">1</div>
<div class="box box2">2</div>
<div class="box box3">3</div>
</div>
Browser support for clip path is pretty decent. Here is a nice little tool for creating clip-path masks.
精彩评论