让我们来谈谈网页设计中的阴影。阴影增添纹理、透视感并强调物体的维度。在网页设计中,使用光影可以增加物理真实感,并用于制作丰富、触觉化的界面。
以下面的登陆页面为例。它是冰岛自行车游的登陆页面。请注意自行车运动员的装饰性投影阴影,以及它是如何让人感觉他们不仅飞越页面上的内容,而且飞越页面本身,仿佛他们“弹出”屏幕。这种感觉充满活力和即时性,非常适合冒险主题。

将其与下面的例子进行比较。这是一个“扁平化”的设计,没有阴影。在这种情况下,自行车本身是焦点。缺乏深度和真实感可以让自行车本身脱颖而出。

您可以欣赏这两种方法之间的差异。使用阴影和深度是一种设计选择;它们应该支持主题以及您希望内容传达的信息。
光与影
正如我们刚才看到的,深度可以增强内容。那么究竟是什么造就了阴影呢?光!
谈论阴影而不谈光是不可能的。它控制着阴影的方向以及阴影的深浅。两者缺一不可。
Google 的 Material Design 设计系统是有效运用光影的一个很好的例子。您肯定遇到过 Material Design 的美学,因为 Google 将其应用于几乎所有产品。



该设计系统借鉴了物理世界,并使用光、表面和投影阴影在三维空间中表达界面。他们关于使用光影的指南 详细介绍了这一点。
在 Material Design 环境中,虚拟光线照亮 UI。关键光线产生更锐利、有方向性的阴影,称为关键阴影。环境光从各个角度出现,产生漫射的、柔和的阴影,称为环境阴影。
阴影是 Material Design 的核心组成部分。将其与 Apple 的 macOS 人机界面指南 进行比较,在 macOS 中,半透明和模糊是唤起深度的主要因素。


在这种情况下,光仍然是一个有影响力的因素,因为它允许元素融入桌面,甚至融入 UI 中的其他面板。同样,在您的界面中使用它也是一种设计选择。无论哪种方式,您都可以看到光如何影响对深度的视觉感知。
光源和颜色
现在我们已经理解了光与阴影之间的关系,我们应该更深入地了解光是如何影响阴影的。我们已经看到光线的强度是如何产生不同深度的阴影的。但关于光线影响阴影的**方向**和**颜色**的方式,还有很多值得探讨。
当光线照射到物体上时,会产生两种阴影,即**投影阴影**和**形体阴影**。

投影阴影
当物体阻挡光源时,就会产生**投影阴影**。投影阴影的色调和明暗度可能有所不同。颜色术语可能很密集且令人困惑,所以让我们先谈谈**色调**和**明度**。
色调是色相与灰色的混合。明度描述了颜色的整体亮度或暗度。明度在绘画中非常重要,因为它是在艺术家将光线和物体关系转换为颜色的过程中使用的。

在网页设计领域,这些颜色的方面是颜色选择器 UI 的内在组成部分。

形体阴影
另一方面,**形体阴影**是指物体背对光源的一侧。形体阴影的边缘比投影阴影更柔和、更不清晰。形体阴影说明了物体的体积和深度。
阴影的外观取决于光线的方向、光线的强度以及物体与阴影投射到的表面之间的距离。光线越强,阴影越深越锐利。光线越柔和,阴影越暗淡越柔和。在某些情况下,对于方向光,我们会得到两个不同的阴影。本影是光线被遮挡的地方,半影是光线被散射的地方。

如果表面靠近物体,阴影会更锐利。如果表面距离较远,阴影会更暗淡。这并不是什么抽象的科学内容。这是我们每天都会遇到的东西,无论你是否意识到它。

光线也可能从物体的侧面或其他表面反射。明亮的表面反射光线,黑暗的表面吸收光线。
这些是理解网页设计中最有价值的光线方面。光线的物理学是一个复杂的话题,我在这里只是简单地提到了其中的一些。如果您想查看基于不同光源投射的阴影的明确示例,这份关于漫画阴影绘制的指南 很有指导意义。
光源定位
请记住,阴影与光线密不可分,因此定义光源——即使实际上不存在光源——是创造令人印象深刻的阴影效果的方法。诀窍是根据光源始终如一地添加阴影。位于元素上方的光源会在元素下方投射阴影。将光源置于元素左侧会在右侧投射阴影。将多个光源置于元素的顶部、底部、左侧和右侧实际上根本不会投射阴影!

光源可以投射到您选择的任何方向。只需确保在您的设计中始终如一地使用它,以便一个元素上的阴影与页面上的其他阴影相匹配。
海拔高度
阴影还可以传达**海拔高度**。再次以 Material Design 为例,因为它展示了如何使用阴影在元素之间创建感知的分离。

内阴影
说到海拔高度,box-shadow
属性是唯一可以为下沉效果创建内阴影的属性。因此,元素看起来不是向上提升,而是被压入。这要归功于inset
关键字。
这对于诸如单击按钮时看起来物理上按下按钮的效果很有用。
也可以使用一些技巧“伪造”内文字阴影,这些技巧在大多数浏览器中都得到了支持。
阴影分层
我们并不局限于每个元素只有一个阴影!例如,我们可以在box-shadow
属性上提供一个用逗号分隔的阴影列表。我们为什么要这样做呢?其中一个原因是为了创建更平滑的阴影。
另一个原因是为了创建有趣的视觉效果。
阴影分层甚至可以使用text-shadow
属性来增强排版效果。
需要注意的是,对于filter: drop-shadow()
,阴影分层的方式略有不同。它的语法也接受列表,但使用空格分隔而不是逗号分隔。
.box {
box-shadow:
0 2px 2px #555, /* commas */
0 6px 5px #777,
0 12px 10px #999
;
}
.box {
filter:
drop-shadow(0 2px 2px #555) /* spaces */
drop-shadow(0 6px 5px #777)
drop-shadow(0 12px 10px #999);
}
还有一点?阴影会叠加在一起,按照声明的顺序排列,其中最上面的阴影是列表中的第一个。

你可能已经猜到drop-shadow()
在这里的工作方式有点不同。阴影呈指数级增加,即2^阴影数量 - 1
。
以下是它的工作原理
- 1 个阴影 = (2^1 – 1)。渲染一个阴影。
- 2 个阴影 = (2^2 – 1)。渲染三个阴影。
- 3 个阴影 = (2^3 – 1)。渲染七个阴影。
或者,用代码表示
.one-shadow {
filter: drop-shadow(20px 20px 0 grey);
}
.three-shadows {
filter:
drop-shadow(20px 20px 0 grey)
drop-shadow(40px 0 0 yellow);
}
.seven-shadows {
filter:
drop-shadow(20px 20px 0 grey)
drop-shadow(40px 0 0 yellow);
drop-shadow(80px 0 0 red);
}
<feDropShadow>
元素对 SVG 的工作方式完全相同。
阴影和可访问性
以下是一些需要思考的内容:阴影可以帮助提高可访问性。
Google 进行了一项针对低视力参与者的研究,以更好地了解阴影和轮廓如何影响个人识别和交互组件的能力。他们发现,使用阴影和轮廓
- 增加了在扫描页面时查找组件的便捷性和速度,并且
- 提高了用户确定组件是否可交互的能力。
那不是一项广泛的科学研究,所以让我们转向看看 W3C 在其针对WCAG 2.0 标准的指南中怎么说
[...] 设计师可能会使字母后面的背景变暗,或在字母周围添加一个细黑色轮廓(至少 1 个像素宽),以使字母与背景之间的对比度比率保持在 4.5:1 以上。
这指的是浅色背景上的浅色文本。WCAG 建议文本和图像之间的对比度比率至少为 4.5:1。您可以使用文本阴影来增加它们之间的对比度。


阴影和性能
在深入研究阴影并在所有事物上添加阴影之前,值得注意的是它们确实会影响性能。
例如,filter: drop-shadow
会被一些浏览器硬件加速。可能会为该元素创建一个新的合成层,并卸载到 GPU。您不希望有太多层,因为这会占用有限的 GPU 内存,并最终导致性能下降。您可以在浏览器的 DevTools 中评估这一点。
模糊是一项代价高昂的操作,因此请谨慎使用。当您模糊某些内容时,它会混合输出像素周围所有像素的颜色以生成模糊的结果。例如,如果您的<blur-radius>
参数为 2px,则过滤器需要查看每个输出像素周围每个方向的两个像素以生成混合颜色。这会针对每个输出像素发生,这意味着大量的计算呈指数级增长。因此,具有较大模糊半径的阴影通常比其他阴影渲染速度慢。
您知道吗?
您知道阴影不会影响文档布局吗?
阴影与目标元素的大小相同。您可以修改box-shadow
的大小(通过扩展半径参数),但其他属性无法修改阴影大小。
您是否知道阴影的z-index
隐式地低于元素?这就是为什么阴影位于其他元素下方。
剪辑和蒙版呢?如果具有box-shadow
的元素被剪辑(使用clip-path
)或使用蒙版(使用mask
),则不会显示阴影。相反,如果具有text-shadow
或filter: drop-shadow()
的元素被剪辑,则会显示阴影,只要它在剪辑区域内即可。
还有一个:我们无法使用阴影属性创建斜阴影(带有对角线)。这需要创建一个阴影元素并在其上使用transform:skew()
。
哦,还有一个:box-shadow
遵循border-radius
。如果元素具有圆角,则阴影也会是圆角。换句话说,阴影会镜像盒子的形状。另一方面,filter: drop-shadow()
可以创建不规则形状,因为它会尊重透明度并遵循内容的形状。

不同类型阴影的最佳使用场景
实际上,网络上的任何内容都可以拥有阴影,并且有多个 CSS 属性和函数可以创建阴影。但是,选择正确的阴影类型才能使阴影有效。
让我们评估一下选项
box-shadow
:此 CSS 属性创建符合元素边界框的阴影。它用途广泛,可以用于从卡片到按钮到几乎任何需要阴影简单地遵循元素框的地方。text-shadow
:这是一个 CSS 属性,专门为文本元素创建阴影。filter: drop-shadow()
:此处的 CSS 属性是filter
,但创建阴影的是它接受的drop-shadow
函数。使这种类型的阴影与例如box-shadow
不同的原因在于,它遵循任何元素(包括伪元素)的渲染形状。<feDropShadow>
:这实际上是一个 SVG 元素,而其余的是 CSS 属性。因此,您将使用它在 SVG 标记中直接创建投影。
一旦您掌握了不同类型的阴影以及每种阴影的独特阴影创建能力,阴影效果的可能性就会变得无限。从简单的投影到浮动元素,甚至内部阴影,我们都可以创建有趣的视觉效果,为 UI 添加额外的意义或价值。
文本阴影也是如此。
现实中的阴影
阴影无处不在。我们一直看到它们以新的、有趣的方式被使用。
您最近是否听说过流行词“拟态设计”?这都与阴影有关。以下是 Maria Muñoz 的一个实现
Yuan Chuan,他创作令人惊叹的生成艺术,称阴影为 UI 设计中的“秘密武器”
CSS 依赖于浏览器中现有的 DOM 结构。除了
::before
和::after
之外,无法生成新的元素。有时我真的希望 CSS 能够直接做到这一点。然而,我们可以通过完全在 CSS 中创建各种阴影和渐变来部分弥补这一点。
这就是为什么拥有
drop-shadow
如此令人兴奋的原因。结合text-shadow
和box-shadow
,我们可以做更多的事情。
看看他是如何使用投影来创建复杂的图案的。
是的,这很疯狂。说到疯狂,值得一提的是,变得过于疯狂会导致性能下降,因此请谨慎操作。
伪元素呢?
哦,是的,阴影属性受::before
和::after
伪元素的支持。
其他尊重阴影的伪元素?::first-letter
伪元素接受box-shadow
和text-shadow
。::first-line
伪元素接受text-shadow
。
看看 Jhey Tompkins 如何在伪元素上使用box-shadow
来创建动画加载器,非常有创意。
阴影动画
是的,我们可以让它们动起来!我们这里介绍的属性和函数完全兼容 CSS 动画和过渡。这意味着我们可以移动阴影、模糊阴影、扩展/缩小阴影(使用box-shadow
)以及改变颜色。
阴影动画可以为用户提供一个提示,表明某个元素是交互式的,或者某个操作已经发生。我们在之前的按钮示例中看到,inset
阴影表明按钮已被按下。另一种常见的动画模式是在悬停时提升卡片。
如果想要优化动画性能,避免对box-shadow
进行动画!对drop-shadow()
进行动画的性能更高。但是,如果想要最流畅的动画,使用一个小技巧是最佳选择!添加一个带有更大box-shadow
的::after
伪元素,并对其opacity
进行动画。
当然,还有很多其他的东西可以进行动画。我将把探索留给你们!
总结
哇,谁知道像 CSS 阴影这样看似“简单”的东西竟然有这么多内容!有光源和阴影的投射方式。不同类型的阴影及其颜色。有使用阴影来营造深度、提升元素和内嵌元素。还有将阴影叠加在其他阴影上的事实。以及我们有一系列 CSS 属性可用于不同的用例。然后,还有随之而来的可访问性和性能影响。而且,嘿,动画也是一回事!这真是太不可思议了!
无论如何,希望这个广泛的概述能让你有所收获,或者至少能帮助你复习一些概念。
感谢您撰写这篇全面的文章。关于
drop-shadow
。您提到它们是呈指数级添加的。我不确定我是否完全理解您的意思。您能详细说明一下吗?也许可以举一些例子来说明它的含义。嗨,Konstantin,
您可以查看下面的 CodePen 以获取说明。
filter:drop-shadow() CodePen
您可以将过滤器视为图像转换管道。
对于这段代码。
这是算法对过滤器执行的操作的大致轮廓。
非常喜欢阅读这篇关于阴影和光的文章。一些效果非常棒,我需要尝试一下。一直都依赖这里。;)
谢谢 George :-)
哇,非常鼓舞人心的文章。
唯一没有清晰表达的部分是关于使用
drop-shadow()
时“阴影是如何呈指数级添加的”。谢谢 Josh。
您可以查看下面的 CodePen 以了解为什么对于
filter:drop-shadow()
,阴影是呈指数级添加的。https://codepen.io/robjoeol/full/eYBeKWo
感谢这篇文章,您没有谈到并发阴影,这可能吗?
我的意思是,一个 DIV 投射光线,另一个 DIV 投射阴影。然后光线吞噬/移除阴影。
我认为 DIV 效果会叠加,但不会被移除?
drop-shadow
和box-shadow
之间的另一个区别是,前者只接受px
单位,而后者接受像素和任何其他长度单位(mm, cm, in, pt, pc
)。