box-shadow属性探秘

目录
  1. 导读
  2. box-shadow
    1. offsetX
    2. offsetY
    3. blur
    4. spread
    5. color
    6. position
  3. 多重阴影
  4. 有趣的效果
    1. 纸张投影效果
      1. 准备元素
      2. 添加阴影样式
      3. 给阴影加个角度并收缩一些
    2. 升起效果
      1. 准备元素和阴影
      2. 添加动画
  5. box-decoration-break

导读

我总是记不住css3的box-shadow属性拥有几个值, 它们的顺序究竟如何? 对我来说, 这是一个大难题. 我们都知道, 使用一个属性, 总是可以不停地在开发者工具上测试UI表现, 直到表现令人满意. 好吧, 有些时候它是奏效的; 而其它时候呢, 我们会消耗时间, 积攒疲劳值, 以及成就感下降等. 一旦短时记忆失效, 我们完全有可能重复一遍之前不愉快地尝试. 因此我选择多花些时间, 用力一次记住它. 如果你恰好也有同样的疑惑, 请读下去.

box-shadow

box-shadow用于创建阴影, 使得元素更有立体感, 它的值由以下六个部分组成:

box-shadow: offsetX offsetY blur spread color position;

它们分别为: x轴偏移 y轴偏移 模糊半径 大小 颜色 位置.

xy轴偏移, 参照css中的坐标系, 水平向右(→)为X轴正方向, 垂直向下(↓)为Y轴正方向.

offsetX

即水平偏移, 值取正数时, 阴影位于元素右边, 值取负数时, 阴影位于元素左边.

为了便于观察到效果, 我将模糊半径默认设置成10px.

box-shadow: 20px 0 10px 0 lightblue; /*阴影向右偏移20px*/

offsetX为正值

box-shadow: -20px 0 10px 0 lightblue; /*阴影向左偏移20px*/

offsetX为负值

offsetY

即垂直偏移, 值取正数时, 阴影位于元素下方, 值取负数时, 阴影位于元素上方.

box-shadow: 0 10px 10px 0 lightblue; /*阴影向下偏移10px*/

offsetY为正值

box-shadow: 0 -10px 10px 0 lightblue; /*阴影向上偏移10px*/

offsetY为负值

blur

设置阴影的模糊半径, 值越大时, 阴影就越模糊, 值为0时则完全不模糊, 值小于0时则按照0处理.

我试着加大模糊半径, 取值为50px, 可以看到阴影变得非常模糊.

box-shadow: 20px 0 50px 0 lightblue; /*阴影向右偏移20px, 模糊半径由10px放大至50px*/

offsetX+blur

同时, 上下边界也有模糊阴影, 理论上讲, 模糊半径在上下左右各个方向应该都有效果, 下面我们来验证之:

box-shadow: 0 0 50px 0 lightblue; /*模糊半径设置为50px, 无偏移*/

blur

如上图, 同猜想一致, 模糊阴影在上下左右4个方向分别发散. 此时, 对角线方向上阴影是最淡的, 要想模糊阴影均匀分布在元素周围, 只需将元素设置为圆形即可.

box-shadow: 0 0 50px 0 lightblue; /*模糊半径设置为50px, 无偏移*/
border-radius: 100%; /*元素设置为圆形*/

blur-circle

spread

设置阴影大小. 当blur值为0时, spread就像是元素背后的一块幕布, spread值越大, 阴影越宽, 当其取负值时, 阴影大小为元素高宽分别减去spread值, 此时, blur设置的模糊阴影则会向内靠拢.

box-shadow: 0 0 0 10px lightblue; /*阴影大小设置为+10px*/

spread

box-shadow: 0 0 10px 10px lightblue; /*模糊半径设置为10px, 阴影大下为+10px*/

spread

box-shadow: 0 0 10px -1px lightblue; /*模糊半径设置为10px, 阴影大下为-1px, 由于模糊阴影部分向内靠拢, 阴影变得非常薄*/

spread+blur

不知道你有没有注意到 ,对于阴影大小, 我使用的是+10px 或者 -1px这样的单位, 这是为什么呢? 这里卖个关子先, 请看如下效果.

box-shadow: 150px 0 0 0px lightblue;

offsetX+spread

仅仅将阴影水平向右移动一段距离, 可见, 阴影是有默认大小的, 并且默认与元素是一般大小. 而这几乎打破了我一度的认知, 说好的阴影呢, 不是环绕吗!

前面提到了圆形阴影, 实际上, 就是border-radius:100%的特例, 那么如果border-radius是一个具体的值呢, 此时阴影又该当如何呈现? 请耐心往下看, 我将在多重阴影的节点给出分析.

color

设置阴影的颜色. 支持常用色值, HEX(16进制), RGB, RGBA, HSL, HSLA等颜色单位. 以下颜色全部都是浅蓝色.

box-shadow: 0 0 10px 0 lightblue;
box-shadow: 0 0 10px 0 #add8e6;
box-shadow: 0 0 10px 0 rgba(173, 216, 230, 1);
box-shadow: 0 0 10px 0 hsla(195, 53%, 79%, 1);

position

设置阴影的位置, 默认为外部阴影, 可通过inset值来设置内部阴影.

box-shadow: 0 0 20px 10px lightblue; /*默认为外部阴影*/
box-shadow: 0 0 20px 10px lightblue inset; /*设置为inset时, 为内部阴影*/

inset

多重阴影

box-shadow同background属性一样, 它们都支持多重效果的设置, 且多重值以逗号分隔.

box-shadow: 0 0 20px 10px lightblue,
            0 0 20px 10px lightblue inset; /*同时设置内外阴影*/

内外阴影

box-shadow: 10px 10px 0px 10px #d0268c,
            -10px -10px 0px 10px rgba(95, 167, 44, 0.56),
            0px 0px 0px 20px lightgrey; /*多重阴影效果*/

多重阴影

上面留下了一个问题, 答案就在下面的样式中.

border-radius: 10px;
box-shadow: 110px 0 0 -10px #ccc, 220px 0 0 0 #808080, 360px 0 0 10px grey;

直接上效果.

border-radius影响spread的效果

从第一个阴影开始(上图左二), 随着阴影spread值的变化, 阴影经历了边框直角, 边框圆角, 边框更圆角(词穷)的过程.

这两个属性的关系如下:

  • spread值越大, border-radius的值就越大, 当spread<0时, border-radius会变小, 最小为0.

有趣的效果

纸张投影效果

先来看以下纸张投影效果是个什么样.

paper-shadow

不就是在纸张底部加个投影吗, 是的, 你没看错. 这样的投影, 实现起来灰常简单, 只需要元素底部左右各加一个box-shadow, 然后佐以transform变换, 稍微改变个角度就大功告成了. 下面我们来用三步实现它.

准备元素
<style>
  .drop-shadow {
    position: relative;
    width: 300px;
    height: 200px;
    background: #CCC;
  }
  .vertical-line{
    position: relative;
    height: 96%;
    width: 0;
    top: 2%;
    margin: 0 auto;
    border: 1px dashed #808080;
  }
</style>
<div class="shadow">
  <div class="vertical-line"></div>
</div>

阶段性效果如下:

paper-shadow-part01

添加阴影样式

先在元素底部左右两边各生成一个阴影, 阴影应该是垂直向下的, 和模糊的, 那么属性如下设置.

.shadow::before, .shadow::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    width: 50%;
    height: 10%;
    box-shadow: 0 15px 20px rgba(125, 125, 125, 0.9);
}
.drop-shadow::after{
  right: 0;
  left: auto;
}

阶段性效果如下:

paper-shadow-part02

这个时候, 阴影基本上呈现了, 但有两点不太完美:

  • 左右阴影应该是倾斜的;
  • 底部阴影有些太厚, 且边缘部分应该淡化.

倾斜可使用 transform: rotate(5deg) 实现. 阴影太厚或边缘淡化只需将阴影往内收一些就行, 如 bottom: 20px; left:10px 等.

给阴影加个角度并收缩一些
.shadow::before, .shadow::after {
    content: "";
    position: absolute;
    bottom: 20px;
    left: 10px;
    width: 50%;
    height: 10%;
    box-shadow: 0 20px 30px rgba(125, 125, 125, 0.9);
    transform: rotate(-5deg);
}
.drop-shadow::after{
  right: 10px;
  left: auto;
  transform: rotate(5deg);
}

阶段性效果如下:

paper-shadow-part03

可以看到, 阴影出现在元素之上. 可设置z-index 为-1, 将阴影层级降低一些, 这样就实现了上述所说的纸张阴影效果.

paper-shadow

升起效果

同样, 我们先来看下升起效果长什么样. 如下:

paper-shadow-part02

这是一个简单的动画, 鼠标移入, 元素上移, 同时阴影缩小, 鼠标移出则反之. 这里, 我们分两步来实现它.

准备元素和阴影
<style>
  .rose {
    position: relative;
    width: 80px;
    height: 120px;
    background: rgba(0,0,0,0.2);
    transition: transform 1s;
  }
  .rose::after {
    content: "";
    position: absolute;
    bottom: -30px;
    left: 50%;
    height: 8px;
    width: 100%;
    border-radius: 50%;
    background-color: rgba(0,0,0,0.2);
    transform: translate(-50%, 0);
    transition: transform 1s;
    box-shadow: 0px 0px 15px 0px rgba(0,0,0,0.2);
  }
</style>
<div class="rose"></div>
添加动画
.rose:hover {
    transform: translateY(-40px);
    transition: transform 1s;
}
.rose:hover::after {
  transform: translate(-50%, 40px) scale(0.75);
  transition: transform 1s;
}

box-decoration-break

欲了解这个属性, 我们先读一则规范.

2016年1月14日, W3C发布了CSS片段模块(CSS Fragmentation Module Level 3)的候选推荐标准, 明确定义了在盒间、盒内、行间、页间进行断行的属性和规则.

简言之, 对于一个盒子模型, 如果它被分裂成多个长度不等的小碎片, 那么它将遵循以下规则来调整布局:

  • 布局将在每个碎片中生效, 并在断点处衔接起来. 但是重新计算大小和位置.
  • 后续碎片必须遵循定位规则, 并且不能高于前面碎片的边缘. 如果想延续碎片开始时的边缘, 需指定 box-decoration-breakclone, 那么padding 和 border 将包裹后续碎片的边缘.

也就是说, 当行内元素换行后, border, background将会出现截断现象, box-shadow也会如此. 如下:

box-decoration-break:slice

实际上, 这种效果并非不可改变, 设置 box-decoration-break:clone , 以上各个css效果将每行重新渲染, 彼此相互独立.

box-decoration-break:clone

box-decoration-break属性用于描述盒子碎片(如跨行的inline元素的各个部分)如何渲染上述border, background, box-shadow等css效果. 该属性拥有两个值, slice 和 clone, 默认值为slice, 如上图一效果. 以下是官方原文.

For box-decoration-break: slice, backgrounds (and border-image) are drawn as if applied to a composite box consisting of all of the box’s fragments reassembled in visual order. This theoretical assembly occurs after the element has been laid out (including any justification, bidi reordering, page breaks, etc.). To assemble the composite box…

从box-decoration-break属性的支持性来看, 目前firefox遥遥领先, 它从v32版本开始就已经全部支持. 以下浏览器均需要 -webkit-前缀, 并且不支持跨列和跨页的截断效果, 其他浏览器目前还不支持.

Chrome Safari Opera ios Safari Opera mini Android Chrome Android QQ
22+ 6.1+ 15+ 7.1+ all 4.4+ 55+ 1.2+

注: 不支持box-decoration-break的浏览器默认按照box-decoration-break:slice 效果来渲染盒子碎片.


本问就讨论这么多内容,大家有什么问题或好的想法欢迎在下方参与留言和评论.

本文作者: louis

本文链接: http://louiszhai.github.io/2017/02/12/box-shadow/

参考文章

Fork me on GitHub