微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

CSS魔法堂:Box-Shadow没那么简单啦:)

2016-05-26 20:31 伯乐在线/黄智安

(点击上方公众号,可快速关注)


作者:伯乐在线专栏作者 - ^-^肥仔John

链接:http://web.jobbole.com/86168/

点击 → 了解如何加入专栏作者


前言


说起box-shadow那第一个想法当然就是用来实现阴影,其实它还能用于实现其他好玩的效果的,本篇就打算说说box-shadow的那些事。


二话不说看效果


3D小球



<style type="text/css">

.ball{

  background: rgba(100,100,100,0.2);

  width: 100px;

  height: 100px;

  padding: 10px;

  border-radius: 50%;

  box-shadow: -14px 8px 100px #333 inset,

              0 0 2px #888,

          3px -1px 4px #444;

}

</style>

<div class="ball"></div>


纸张阴影(来自@张鑫旭老师)



<style type="text/css">

.curved_box {

    display: inline-block;

    *display: inline;

    width: 200px;

    height: 248px;

    margin: 20px;

    background-color: #fff;

    border: 1px solid #eee;

    -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 60px rgba(0, 0, 0, 0.06) inset;

    -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.06) inset;

    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.06) inset;

    position: relative;

    *zoom: 1;

}

 

.curved_box:before {

    -webkit-transform: skew(-15deg) rotate(-6deg);

    -moz-transform: skew(-15deg) rotate(-6deg);

    transform: skew(-15deg) rotate(-6deg);

    left: 15px;

}

.curved_box:after {

    -webkit-transform: skew(15deg) rotate(6deg);

    -moz-transform: skew(15deg) rotate(6deg);

    transform: skew(15deg) rotate(6deg);

    right: 15px;

}

 

.curved_box:before, .curved_box:after {

    width: 70%;

    height: 55%;

    content: ' ';

    

    -webkit-box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);

    -moz-box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);

    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);

    

    position: absolute;

    bottom: 10px;

    z-index: -1;    

}

</style>

<div class="curved_box"></div>


细读属性


看到上面这么绚丽的效果,是不是迫不及待想弄清box-shadow呢?下面我们来一步步解密它吧!


概述属性语法


box-shadow: none | <shadow>[,<shadow>]*

默认值为none

<shadow>:inset? && <length>{2,4} && <color>?

shadow pattern,默认为outset,即采用outer box-shadow。通过设置为inset时,则采用inner box-shadow。

horizontal offset,阴影距离原位置的水平位移,正数表示向右移动,负数表示向左移动。

vertical offset,阴影距离原位置的垂直位移,正数表示向下移动,负数表示向上移动。

blur radius,默认值为0,阴影模糊度半径。

spread distance,默认值为0,扩展或缩小阴影的作用面积。

<color>,阴影颜色,默认与color属性一致。


注意:我们可以同时设置多个阴影,而阴影的z-index值从左向右递减。


outer box-shadow 和 inner box-shadow怎么玩?


默认情况下采用的是outer box-shadow,当在box-shadow中添加inset关键词后,则采用inner box-shadow了,但到底它俩的效果是如何的呢?



<style type="text/css">

.box{

  float: left;

 

  background: #888;

  width: 100px;

  height: 100px;

  margin-right: 20px;

}

.outer-box-shadow{

  box-shadow: 10px 10px #F00;

}

.inner-box-shadow{

  box-shadow: 10px 10px #F00 inset;

}

</style>

<div class="box outer-box-shadow"></div>

<div class="box inner-box-shadow"></div>

</div>


outer-box-shadow

特点:阴影落在元素的border box之外。

实现原理:


  1. 创建一个与元素border box尺寸一致的阴影盒子;

  2. 将阴影盒子定位到于元素border box重合,并位于元素之下;

  3. 根据horizontal offset和vertical offset来相对原位置作移动;

  4. 根据spread distance缩放阴影盒子的尺寸(会影响盒子的位移);

  5. 根据blur radius对阴影盒子作加工;

  6. 最后将阴影盒子与元素border box重合部分剪切掉。



<style type="text/css">

.box{

  background: #888;

  width: 100px;

  height: 100px;

}

.outer-box-shadow{

  box-shadow: 90px 10px #F00;

}

</style>

<div class="box outer-box-shadow"></div>

</div>


模拟一下:



<style type="text/css">

.box{

  position: relative;

}

.box-shadow{

  position: absolute;

  z-index: -1;

  background: #F00;

  width: 100px;

  height: 100px;

  left: 20px;

  top: 20px;

}

.box-content{

  background: #888;

  width: 100px;

  height: 100px;

}

</style>

<div class="box">

  <div class="box-shadow"></div>

  <div class="box-content"></div>

</div>


inner-box-shadow


特点:阴影落在元素的padding box之内。

实现原理(纯个人理解):


  1. 创建一个与元素padding box尺寸一致的阴影盒子;

  2. 将阴影盒子定位到于元素padding box重合,并位于元素之上;

  3. 水平和垂直各画两条线,分别跟元素padding edge重合;(共4条分别记为left/top/right/bottom-guideline)

  4. 根据horizontal offset和vertical offset移动left/top/right/bottom-guideline。

  5. 根据spread distance移动4条线。spread distance为正数时,left-guideline向右移动,top-guideline向下移动,right-guideline向左移动和bottom-guidelien向上移动;spread distance为负数时,则相反。

  6. 根据blur radius加工元素各padding edge至其对应的guideline间的区域.

  7. 对阴影盒子进行剪裁

    1. 剪切掉不与元素padding box重叠的部分;

    2. 仅显示元素各padding edge至其对应的guideline间的区域。



<style type="text/css">

.box{

  float: left;

  background: #888;

  width: 100px;

  height: 100px;

  margin-right: 10px;

}

.box1{

  box-shadow: 0 0 0 20px red inset;

}

.box2{

  box-shadow: 10px 0 0 20px red inset;

}

.box3{

  box-shadow: 10px 0 10px 20px red inset;

}

.box4{

  box-shadow: 0 0 10px 50px red inset;

}

</style>

<div class="box box1"></div>

<div class="box box2"></div>

<div class="box box3"></div>

<div class="box box4"></div>


模拟一下:



<style type="text/css">

.box-shadow{

  position: relative;

  display: inline-block;

  background: red;

  overflow: hidden;

}

.bg{

  position: absolute;

  background: #888;

  left: 30px;

  right: 10px;

  top: 20px;

  bottom: 20px;

}

.content{

  position: relative;

  z-index: 1;

  width: 80px;

  height: 80px;

  padding: 20px;

}

</style>

<div class="box-shadow">

  <div class="bg"></div>

  <div class="content"></div>

</div>


模糊边框 by blur radius


W3C spec中没有规定浏览器厂商使用哪种方式实现模糊效果,反正效果与高斯模糊效果差不多就是了。但有一点我们需要注意的,那就是模糊效果会扩大阴影的面积。



<style type="text/css">

.outline{

  border: 1px solid red;

  margin: 40px 0;

}

.s{

  background: rgba(255, 100, 100, 0.1);

  width: 100px;

  height: 100px;

}

.s1{

  box-shadow: 110px 0 0 #333;

}

.s2{

  box-shadow: 110px 0 20px #333;

}

.s3{

  box-shadow: 110px 0 40px #333;

}

</style>

<div class="outline">

  <div class="s s1">sample1</div>

</div>

<div class="outline">

  <div class="s s2">sample2</div>

</div>

<div class="outline">

  <div class="s s3">sample3</div>

</div>


sample1是blur radius为0的效果,可以看到阴影尺寸与元素尺寸一模一样。而sample2是blur radius为20px的效果,可以看到阴影尺寸有所扩展了,而sample3则扩展得更多一些。


现在我们感性上认知到blur radius值大于0时会扩展阴影尺寸,那么到底扩展多少呢?那我们要先明确模糊发生的起始位置了。


  1. 对于outer-shadow-box而言,模糊发生的起始位置就是阴影盒子的各边;

  2. 对于inner-shadow-box而言,模糊发生的起始位置就是各guideline。


然后模糊效果是从发生的位置,对于水平方向的边或guideline则向垂直方向发散,对于垂直方向的边或guideline则向水平方向发散,且发散的距离相同。

发散的距离相同,因此每个方向各发散为blur radius/2的距离。看sample3中阴影尺寸已经与元素盒子重叠了,因为阴影盒子左边框向左发散了20px了,超过它俩之间10px的水平距离了,而sample2则恰恰邻近而已。

缩放阴影尺寸 by spread distance


如果说blur radius是暗地里扩大阴影的尺寸,那么spread distance则是明目张胆地缩放阴影的尺寸了。



<style type="text/css">

.outline{

  border: 1px solid red;

  margin: 40px 0;

}

.s{

  background: rgba(255, 100, 100, 0.1);

  width: 100px;

  height: 100px;

}

.s1{

  box-shadow: 110px 0 0 #333;

}

.s2{

  box-shadow: 110px 0 0 10px #333;

}

.s3{

  box-shadow: 110px 0 0 -10px #333;

}

</style>

<div class="outline">

  <div class="s s1">sample1</div>

</div>

<div class="outline">

  <div class="s s2">sample2</div>

</div>

<div class="outline">

  <div class="s s3">sample3</div>

</div>


还记得《CSS魔法堂:重拾Border之——解构Border》中提及通过border-top/right/bottom/left-colors实现彩虹边框吗?由于兼容性问题和1px对应一种color的缘故,实际应用得很少,但通过outer-box-shadow和spread distance我们就可以得到效果更好,兼容性很高的实现方案了。



<style type="text/css">

.rainbow{

  margin: 50px;

  width: 100px;

  height: 100px;

  box-shadow: 0 0 0 2px rgb(255,0,0),

              0 0 0 5px rgb(255,165,0),

              0 0 0 8px rgb(255,255,0),

              0 0 0 10px rgb(0,255,0),

              0 0 0 12px rgb(0,127,255),

              0 0 0 15px rgb(0,0,255),

              0 0 0 20px rgb(139,0,255);

}

</style>

<div class="rainbow"></div>


弄清各图层的z-index



上图可以看到没有阴影时,各图层的z-index顺序。那么阴影呢?


  1. 对于outer-box-shadow,则其z-index高于margin图层,低于background-color图层;

  2. 对于inner-box-shadow,则其z-index高于padding图层,低于content图层。


阴影的position


通过horizontal/vertical offset重定位阴影盒子,通过blur radius或spread distance缩放阴影盒子的尺寸,但请注意的是阴影盒子不影响其他盒子的布局,其实阴影盒子就相当于采用absolute定位一样,不会占据Normal flow的空间,也不会影响其他元素的布局,因此仅修改阴影位置或尺寸时,只会触发repaint,而不会触发reflow。


圆角or直角box-shadow傻傻分不清楚?


阴影不仅默认尺寸与元素盒子一致,默认形状也一致。也就是元素盒子采用圆角时,阴影的默认形状也是圆角的。既然说是默认形状一致,就是说可以不一致咯!那到底如何不一致呢,下面我们一起来看个究竟吧!



<style type="text/css">

.s1{

  background: #0EF;

  width: 100px;

  height: 100px;

  border-radius: 10px;

  box-shadow: 110px 0 0 -10px #333,

        220px 0 0 0 #666,

        360px 0 0 20px #888;

}

</style>

<div class="s1">sample1</div>


当设置spread distance后,border-radius的值也将随之变化,具体公式为border-radius + spread-distance * (1 + (border-radius / spread-distance - 1)^3)。

因此spread distance为正数时,border-radius会变大; 而spread distance为负数时,border-radius会减小,直至为0px为止。


被割裂的box-shadow


当设置box-shadow的盒子被拆分为多个盒子时,其对应的box-shadow又会如何呢?其实这不仅仅是box-shadow的问题,如border、background-image等均会遇到同样的问题。CSS3中引入一个新特性box-decoration-break来设置上述情况时的渲染效果。

box-decoration-break: slice | clone

slice是默认值,表示首先按未拆分时的状态渲染border、background-image等样式,然后再将其直接拆分为多个盒子;

clone表示首先将其直接拆分为多个盒子,然后再逐个盒子渲染border、background-image等样式。



<style type="text/css">

.intro{

  font-size: 14px;

  line-height: 1.5;

  text-indent: 1em;

  width: 300px;

}

.intro span{

  border: 1px solid #666;

  border-radius: 5px;

  box-shadow: 5px 3px 3px #AAA;

}

.slice{

  -webkit-box-decoration-break: slice;

}

.clone{

  -webkit-box-decoration-break: clone;

}

</style>

<p class="intro">

<span class="slice">

Hey there, welcome to be here to share something aboute CSS together:) My name is fsjohnhuang, a FE from Midea. Enjoy the evolution of FE, and feel excited in the work I'm doing now.

</span>

</p>

<p class="intro">

<span class="clone">

Hey there, welcome to be here to share something aboute CSS together:) My name is fsjohnhuang, a FE from Midea. Enjoy the evolution of FE, and feel excited in the work I'm doing now.

</span>

</p>


从上面可以看到,与其说box-decoration-break的属性值影响box-shadow的效果,还不如说是box-decoration-break的属性值影响border-radius和border作用到元素盒子的效果,然后由盒子的效果再间接影响box-shadow的效果。


兼容性



IE和Edge均不支持,FF支持得最好,而Webkit内核的则要加-webkit-前缀。

对于不支持的浏览器,其效果如同box-decoration-break:slice


兼容性



IE9都支持box-shadow多让人可喜可贺的消息啊(因为我工作中只需兼容IE9+就Ok了:))。但IE6~8呢?方案很多啦,上面也有简单的介绍到。@张鑫旭老师提到在模拟blur radius效果时,采用以下方案


.ieBlock{

    height:100px;

    width:100px;

    background:#000;

    filter:progid:DXImageTransform.Microsoft.Blur(pixelradius=10);

    -ms-filter:"progid:DXImageTransform.Microsoft.Blur(pixelradius=10)";

}


要比采用以下方案要好!


.shadow {

    -moz-box-shadow: 3px 3px 4px #000;

    -webkit-box-shadow: 3px 3px 4px #000;

    box-shadow: 3px 3px 4px #000;

    /* For IE 8 */

    -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000')";

    /* For IE 5.5 - 7 */

    filter: progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000');

}


另外若想不假思索地用到生产环境中,还是用成熟的CSS库较好。具体请参考

PIE使IE支持CSS3圆角盒阴影与渐变渲染


感谢


  • the-box-shadow

  • break-decoration

  • CSS3 box-shadow实现纸张的曲线投影效果

  • CSS实现跨浏览器兼容性的盒阴影效果

  • CSS实现跨浏览器的box-shadow盒阴影效果(2)

  • PIE使IE支持CSS3圆角盒阴影与渐变渲染

  • 《图解CSS3核心技术与案例实战》 —— 3.5 CSS3盒子阴影属性



专栏作者简介 ( 点击 → 加入专栏作者 


^-^肥仔John:偏前端的临栈工程师

打赏支持作者写出更多好文章,谢谢!



【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!


 
前端大全 更多文章 详解Javascript中的Object对象 结合个人经历总结的前端入门方法 前端不为人知的一面–前端冷知识集锦 一份优秀的前端开发工程师简历是怎么样的? 浅谈Web缓存
猜您喜欢 网易 Android工程模板化实践 拿代码量算KPI……跟程序员们来这套?(下) 2015年最热门编程语言岗位需求及薪资分析 R中进行debug有几种不同方式 为什么这段代码输出的是”Hello World”