然而,当我环顾网络时,很明显,大多数阴影并不像它们所希望的那样丰富,网络上覆盖着模糊的灰色盒子,看起来并不像影子。
在本教程中,我们将学习如何将典型的箱形阴影转换为漂亮、逼真的阴影。
我保证,我们很快就会谈到有趣的CSS技巧。但首先,我想退后一步,谈谈为什么阴影存在于CSS中,以及我们如何最大限度地利用它们。
阴影表示高程,而较大的阴影表示更高的高程。如果我们有策略地使用阴影,我们可以创造深度的错觉,就好像页面上的不同元素在不同级别上浮动在背景之上一样。
下面是一个示例。拖动”显示”滑块以查看我的意思:
我希望我构建的应用程序感觉触觉和真实,就好像浏览器是进入不同世界的窗口一样。阴影有助于推销这种幻觉。
这里也有一个战术上的好处。通过在页眉和对话框上使用不同的阴影,我们给人的印象是对话框比页眉更靠近我们。我们的注意力往往会被吸引到离我们最近的元素上,因此通过提升对话框,我们使用户更有可能首先关注它。我们可以使用高程作为引导注意力的工具。
当我使用阴影时,我这样做时会考虑到这些目的之一。要么我想增加特定元素的突出性,要么我想让我的应用程序感觉更有触感和逼真感。
但是,为了实现这些目标,我们需要全面了解应用程序中的阴影。
很长一段时间,我并没有真正正确地使用阴影😬。
当我希望一个元素有一个阴影时,我会添加box-shadow
属性并修改数字,直到我喜欢结果的外观。
问题是:通过像这样孤立地创建每个阴影,你最终会得到一堆不协调的阴影。如果我们的目标是创造深度的错觉,我们需要每一个阴影来匹配。否则,它看起来就像一堆模糊的边框:
在自然界中,阴影是从光源投射而来的。阴影的方向取决于光的位置:
通常,我们应该为页面上的所有元素决定一个光源。该光源通常位于上方且略靠左:
如果 CSS 有一个真正的照明系统,我们将为一个或多个灯光指定一个位置。可悲的是,CSS没有这样的东西。
相反,我们通过指定水平偏移和垂直偏移来移动阴影。例如,在上图中,生成的阴影具有 4px 的垂直偏移量和 2px 的水平偏移量。
这是内聚阴影的第一个技巧:页面上的每个阴影都应该共享相同的比率。这将使每个元素看起来都来自同一光源。
相同的比率? 您可能想知道为什么我建议对每个元素使用相同的比率。难道每个元素都需要有自己的比例,因为每个元素相对于光源都有一个独特的位置吗? 如果光源就在附近,就像人们挤在篝火旁一样,情况确实如此。但如果光源很远,就像太阳一样,这些差异可以忽略不计。一切都会以相同的角度投射阴影。 出于实用性,我选择让所有阴影共享相同的角度,因为尝试为每个元素计算唯一角度对我来说听起来太麻烦了。😅
接下来,让我们更多地讨论高程。我们如何创造一种错觉,即一个元素正在向用户抬起?
我们需要同时调整所有4个变量,以创建一个有凝聚力的体验。
试试这个演示,并注意值是如何变化的:
前两个数字(水平和垂直偏移)串联在一起缩放。垂直偏移始终是水平偏移的 2 倍。
当卡牌升得更高时,还会发生另外两件事:
(我还增加了卡片的大小,以获得更逼真的效果。在实践中,跳过此步骤会更容易。)
这些事情发生的原因可能有复杂的数学原因,但我们可以利用我们作为人类的直觉,存在于一个光明的世界里。
如果你在一个光线充足的房间里,把手按在你的桌子上(或任何附近的表面),然后慢慢抬起。注意阴影是如何变化的:它离你的手更远(更大的偏移量),它变得更模糊(更大的模糊半径),它开始淡出(较低的不透明度)。如果您无法移动双手,则可以改用房间中的参考对象。比较你周围的不同阴影。
因为我们在有阴影的环境中有如此多的经验,所以我们真的不必记住一堆新规则。我们只需要在设计阴影时运用我们的直觉。虽然这确实需要心态的转变;我们需要开始将我们的HTML元素视为物理对象。 所以,总结一下:
1.页面上的每个元素都应由相同的全局光源照亮。
2.box-shadow
属性使用水平和垂直偏移表示光源的位置。为了确保一致性,每个阴影应在这两个数字之间使用相同的比率。
3.当元素靠近用户时,偏移量应增加,模糊半径应增加,阴影的不透明度应降低。
4.您可以使用我们的直觉跳过其中一些计算。
像Blender
这样的现代3D插图工具
可以通过使用一种称为光线追踪的技术来产生逼真的阴影和照明。
在光线追踪中,数百束光从相机中射出,从场景中的表面反弹数百次。这是一种计算成本高昂的技术;生成单个图像可能需要几分钟到几小时!
Web用户没有这种耐心,因此box-shadow
算法更加简陋。它以我们元素的形状创建一个框,并对其应用基本的模糊算法。
因此,我们的阴影永远不会看起来逼真,但是我们可以通过一种漂亮的技术来改善很多事情:分层。
我们将不使用单个框阴影,而是将一些框阴影堆叠在一起,偏移量和半径略有不同:
通过分层多个阴影,我们创造了现实生活中阴影中存在的一些微妙之处。
这种技术在Tobias Ahlin
的精彩博客文章”Smoother and Sharper Shadows with Layered box-shadow“中进行了详细描述。
在这篇博文的后面,我将分享一些以编程方式提出这些值的工具!
性能权衡 不可否认,分层阴影是美丽的,但它们确实是有代价的。如果我们分层 5 个阴影,我们的设备必须多做 5 倍的工作! 这在现代硬件上并不是一个大问题,但它可能会在较旧的廉价移动设备上减慢渲染速度。 与往常一样,请务必进行自己的测试!根据我的经验,分层阴影不会对性能产生重大影响,但我也从未尝试过同时使用数十个或数百个阴影。 此外,尝试对分层阴影进行动画处理可能是一个坏主意。
到目前为止,我们所有的阴影都使用了半透明的黑色,比如.这实际上并不理想。hsl(0deg 0% 0% / 0.4)
当我们在背景颜色上叠加黑色时,它不仅会使它变暗;它也使它变得不饱和。
比较这两个框:
左边的框使用透明的黑色。右侧的框与颜色的色调和饱和度相匹配,但会降低亮度。我们最终得到了一个更有活力的盒子!
当我们对阴影使用较深的颜色时,也会发生类似的效果:
在我看来,这些阴影都不太对劲。左边的饱和度太低,但右边的饱和度不够低;感觉更像是光芒而不是阴影!
可能需要一些实验才能找到金发姑娘的颜色:
通过匹配色调并降低饱和度/亮度,我们可以创建一个没有那种“褪色”灰色质量的真实阴影。
饱和度和亮度之间的关系
如果您熟悉颜色格式,您就会知道饱和度和亮度是独立控制的。
那么,降低亮度似乎也会对饱和度产生影响,是不是有点奇怪?
例如,这里有两个饱和度百分比相等 (100%) 但感知饱和度非常不同的框:
发生这种情况是因为在高/低亮度值下,颜色中没有那么多的“颜料”。饱和度不会对整体颜色产生太大影响。
这在极端情况下最为明显:
hsl(0deg 0% 100%)
为纯白色,饱和度为 0%。hsl(0deg 100% 100%)
也是纯白色,即使完全饱和。如果我们将亮度设置为 95%,则存在差异,但很微妙:
对于非常深的颜色也是如此:
然而,当我们处于亮度光谱的中间时,全范围的饱和度是可用的:
以下是我的看法:50% 亮度是所有色调的“默认”版本。亮度在 50% 时对饱和度没有影响。
当我们从50%的最佳点增加或减少亮度时,我们减少了颜色中可用颜料的量。颜色不可能完全饱和,浅色或深色。
饱和度%是一个相对度量,基于给定亮度下可用的颜料量。
这就是为什么我们之前必须降低阴影示例中的饱和度!亮度移近50%的最佳点,因此可以获得更广泛的饱和度。为了保持感知的生动度不变,我们必须降低饱和度百分比。
在本教程中,我们介绍了 3 个不同的想法:
下面是一个应用所有这些想法的示例:
构建了一个工具,允许您使用这些技术生成阴影。它被称为 “阴影调色板生成器”。
这个工具的灵感很大程度上来自菲利普·布鲁姆(Philipp Brumm)的精彩创作,shadows.brumm.af。
我们看到的阴影需要根据其高程和环境进行自定义。在一个拥有设计系统和有限设计代币的世界里,这似乎适得其反。我们真的能”标记”这些阴影吗?
我们绝对可以!虽然它需要一些现代工具的帮助。
例如,以下是我如何使用 React、styled-components 和 CSS 变量来解决这个问题:
我有一个静态的ELEVATIONS
对象,它定义了 3 个提升。每个阴影的颜色数据使用 CSS 变量--shadow-color
。
每次我更改背景颜色(在Wrapper
和BlueWrapper
中),我也会更改--shadow-color
。这样,任何使用影子的子项都将自动继承此属性。
如果您没有使用CSS变量的经验,这可能看起来像是完全的魔术。不过,这只是一个例子;随意以不同的方式构建事物!
早些时候,我提到我对盒子阴影的策略曾经是“修补值,直到它看起来不错”。老实说,这是我对所有 CSS 的方法。😅
CSS是一种棘手的语言,因为它是隐式的。我了解了所有关于属性的知识,比如position
and flex
and overflow
,但我对驱动它们的原理一无所知,比如“位置”、“弯曲”和“溢出”,但我对驱动它们的原理一无所知,比如堆叠上下文、假设大小和滚动容器。
属性有点像函数参数。它们是布局算法和其他复杂内部机制使用的输入。
几年前,我决定花时间学习CSS是如何工作的。我沿着MDN的兔子洞,偶尔一直钻到坚实的核心。当我遇到一种卑鄙的情况,事情似乎没有意义时,我会解决这个问题,决心戳它,直到我明白发生了什么。这不是一个快速或简单的过程,但天哪,它是有效的。突然之间,事情开始变得如此有意义。CSS是一种奖励那些深入的人的语言。
在大流行开始之前,我开始想,也许我的经验可以帮助其他开发人员加快这一过程。毕竟,我们大多数人都没有时间(或精力!)花费数年时间探索文档和规范。我辞去了Gatsby Inc.
的软件工程师工作,花了一年半的时间构建了最终的CSS课程。几年前我希望我有的课程。
它被称为CSS for JavaScript Developers,它是一个全面的交互式课程,展示了CSS是如何工作的。它是专门为使用React/Angular/Vue等JavaScript框架的开发人员构建的。有超过200节课,分布在10个模块中。您已经完成了其中之一:本关于阴影设计的教程是从课程中改编而来的!不过,在课程中,也有视频,练习和迷你游戏。如果你发现CSS令人困惑或令人沮丧,我想帮助改变这一点。您可以在 css-for-js.dev 上了解更多信息。
在本教程中,我们一直在使用box-shadow
属性。 box-shadow
是一个很棒的全面工具,但它并不是我们在CSS中唯一的阴影选项😮。
看看:filter: drop-shadow
语法看起来几乎相同,但它产生的阴影是不同的。这是因为filter
属性实际上是 SVG过滤器的 CSS挂钩。drop-shadow
使用的是 SVG高斯模糊,这是一种与盒子阴影使用的模糊算法不同的模糊算法。
两者之间还有其他一些重要的区别,但现在我想专注于drop-shadow
的超能力:它勾勒出元素的形状。
例如,如果我们在具有透明和不透明像素的图像上使用它,阴影将仅适用于不透明像素:
这适用于图像,但也适用于HTML元素!看看我们如何使用它来将阴影应用于包含提示的工具提示:
在许多情况下,drop-shadow
比 box-shadow
性能更高,因为filter
属性可以进行硬件加速,这意味着 GPU
可以代替 CPU
来管理它。也就是说,这确实取决于浏览器:我注意到Safari
尤其会与 drop-shadow
作斗争。具体来说,当过滤器应用于包含文本输入的元素时,它似乎不喜欢。它引入了一些输入延迟。
我希望本教程能启发您添加或调整一些阴影!老实说,很少有开发人员将这种水平的想法置于他们的阴影中。这意味着大多数用户不习惯看到郁郁葱葱、逼真的阴影。当我们在阴影中付出更多的努力时,我们的产品就会从人群中脱颖而出。
原文链接:Designing Beautiful Shadows in CSS 译文:如何用 CSS 中写出超级美丽的阴影效果(估计是机译的,译完就不管了,很拉跨)