用 CSS 设计漂亮的阴影

523 阅读10分钟

用 CSS 设计漂亮的阴影

介绍

以我的拙见,最好的网站和 Web 应用程序对它们具有切实的“真实”品质。实现这种质量涉及很多因素,但阴影是一个关键因素。

但是,当我环顾网络时,很明显大多数阴影并不像它们应有的那样丰富。网络被模糊的灰色框覆盖,看起来并不像阴影。

在本教程中,我们将学习如何将典型的框阴影转换为美丽的、栩栩如生的:

目标受众

本教程适用于熟悉 CSS 基础知识的开发人员。假设有一些关于box-shadowhsl()颜色和 CSS 变量的知识。

为什么还要使用阴影?

我保证,我们很快就会接触到有趣的 CSS 技巧。但首先,我想退后一步,谈谈为什么CSS 中存在阴影,以及我们如何使用它们来达到最大效果。

阴影意味着海拔,更大的阴影意味着更高的海拔。如果我们有策略地使用阴影,我们可以创造深度的错觉,就好像页面上的不同元素在不同层次上漂浮在背景之上。

我希望我构建的应用程序具有触感和真实感,就好像浏览器是进入另一个世界的窗口。阴影有助于出售这种错觉。

这里还有一个战术上的好处。通过在标题和对话框上使用不同的阴影,我们创造了对话框比标题离我们更近的印象。我们的注意力往往会被吸引到离我们最近的元素上,因此通过提升对话框,我们更有可能让用户首先关注它。我们可以使用海拔作为引导注意力的工具。

当我使用阴影时,我会考虑到这些目的之一。要么我想增加特定元素的显着性,要么我想让我的应用程序感觉更触手可及、栩栩如生。

但是,为了实现这些目标,我们需要全面了解应用程序中的阴影。

创建一致的环境

很长一段时间,我都没有真正正确地使用阴影😬。

当我想要一个元素有阴影时,我会添加box-shadow属性并修改数字,直到我喜欢结果的外观。

问题是:通过像这样孤立地创建每个阴影,你最终会得到一堆不协调的阴影。如果我们的目标是创造深度错觉,我们需要每个阴影都匹配。否则,它看起来就像一堆模糊的边界:

在自然界中,阴影是从光源投射出来的。阴影的方向取决于灯光的位置:

悬停、聚焦或点击以进行交互

一般来说,我们应该为页面上的所有元素决定一个单一的光源。该光源通常位于上方并略微向左:

如果 CSS 有一个真正的照明系统,我们将为一个或多个灯指定一个位置。可悲的是,CSS 没有这样的东西。

相反,我们通过指定水平偏移和垂直偏移来移动阴影。例如,在上图中,生成的阴影具有 4px 的垂直偏移和 2px 的水平偏移。

这是内聚阴影的第一个技巧:页面上的每个阴影都应该共享相同的比例。这将使每个元素看起来都来自同一个光源。

比例一样吗?

您可能想知道为什么我建议对每个元素使用相同的比率。难道每个元素都不需要有自己的比例,因为每个元素相对于光源都有唯一的位置吗?

如果光源就在附近,就像人们蜷缩在篝火旁一样,情况就是如此。但是如果光源很远,比如太阳,这些差异可以忽略不计。一切都会在同一个角度投下阴影。

出于实用性考虑,我选择让所有阴影共享相同的角度,因为尝试为每个元素计算独特的角度对我来说太麻烦了。😅

接下来,让我们更多地谈谈海拔。我们如何创造一个元素正在向用户提升的错觉?

我们需要同时调整所有 4 个变量以创建有凝聚力的体验。

前两个数字 - 水平和垂直偏移 - 一起缩放。垂直偏移始终是水平偏移的 2 倍。

当卡片升得更高时,还会发生另外两件事:

模糊半径变

阴影变得不透明

(我还增加了卡片的大小,为了更加真实。在实践中,跳过这一步会更容易。)

这些事情发生的原因可能有复杂的数学原因,但我们可以利用我们作为存在于光明世界中的人类的直觉。

如果您在光线充足的房间内,请将手按在桌子(或附近的任何表面)上,然后慢慢抬起。注意阴影是如何变化的:它远离你的手(更大的偏移),它变得更模糊(更大的模糊半径),它开始逐渐消失(更低的不透明度)。如果您无法移动您的手,您可以使用房间中的参考对象来代替。比较你周围不同的阴影。

因为我们在有阴影的环境中有很多经验,所以我们真的不需要记住一堆新规则。在设计阴影时,我们只需要运用我们的直觉。尽管这确实需要转变思维方式;我们需要开始将我们的 HTML 元素视为物理对象。

所以,总结一下:

  1. 页面上的每个元素都应该由相同的全局光源照亮。
  2. box-shadow属性使用水平和垂直偏移表示光源的位置。为确保一致性,每个阴影应使用这两个数字之间的相同比率。
  3. 随着元素离用户越来越近,偏移量应该增加,模糊半径应该增加,阴影的不透明度应该降低。
  4. 您可以使用我们的直觉跳过其中的一些计算。

技巧

分层

Blender 等现代 3D 插图工具可以通过使用称为光线跟踪的技术产生逼真的阴影和照明。

在光线追踪中,数百束光从相机中射出,从场景中的表面反射数百次。这是一种计算成本高的技术;生成单个图像可能需要几分钟到几小时!

网络用户没有那种耐心,所以box-shadow算法要简陋得多。它以我们元素的形状创建一个盒子,并对其应用基本的模糊算法。

因此,我们的阴影永远不会看起来像照片般逼真,但我们可以使用一种巧妙的技术来改善一些东西:分层

我们将不使用单个 box-shadow,而是将一些彼此堆叠在一起,偏移量和半径略有不同:

性能权衡

不可否认,分层阴影是美丽的,但它们确实需要付出代价。如果我们将 5 个阴影分层,我们的设备必须多做 5 倍的工作!

这在现代硬件上不是什么大问题,但它减慢在较旧的廉价移动设备上的渲染速度。

与往常一样,请务必进行自己的测试!根据我的经验,分层阴影不会以显着的方式影响性能,但我也从未尝试过同时使用数十个或数百个。

此外,尝试为分层阴影设置动画可能不是一个好主意。

颜色匹配的阴影

到目前为止,我们所有的阴影都使用了半透明的黑色,比如hsl(0deg 0% 0% / 0.4)。这实际上并不理想。

当我们在背景颜色上叠加黑色时,它不仅会使它变暗;它也会使其饱和度降低很多。

比较这两个框:

使用 Prettier 格式化代码重置代码

通过匹配色调并降低饱和度/亮度,我们可以创建一个真实的阴影,没有那种“褪色”的灰色质量。

饱和度和亮度的关系

如果您熟悉hsl颜色格式,就会知道饱和度和亮度是独立控制的。

那么,降低亮度似乎也对饱和度有影响是不是有点奇怪?

为了回答这个问题,我们需要钻进一个兔子洞。如果您有兴趣,请单击“显示更多”以深入了解!

把这一切放在一起

我们在本教程中涵盖了 3 个不同的想法:

  1. 通过协调我们的阴影创造一个有凝聚力的环境。
  2. 使用分层创建更逼真的阴影。
  3. 调整颜色以防止“褪色”灰色阴影。

这是一个应用所有这些想法的例子:

我有一个静态ELEVATIONS对象,它定义了 3 个高度。每个阴影的颜色数据使用一个 CSS 变量--shadow-color.

每次更改背景颜色(在Wrapper和 中BlueWrapper)时,我也会更改--shadow-color. 这样,任何使用影子的孩子都会自动继承这个属性。

如果您对 CSS 变量没有经验,这可能看起来很神奇。不过,这只是一个例子;随意以不同的方式构建事物!

继续旅程

早些时候,我提到我的框阴影策略曾经是“修改值直到它看起来没问题”。老实说,这是我对所有 CSS 的方法。😅

CSS 是一种棘手的语言,因为它是隐式的。我了解了所有关于属性position和和flex和之类的东西overflow,但我对驱动它们的原理一无所知,比如堆叠上下文和假设大小以及滚动容器。

在 CSS 中,属性有点像函数参数。它们是布局算法和其他复杂内部机制使用的输入。

几年前,我决定花时间学习 CSS 的真正工作原理。我去了 MDN 兔子洞,偶尔一直钻到实心核心*. 当我遇到其中一件事情似乎毫无意义的卑鄙情况时,我会解决这个问题,决心戳戳它,直到我明白发生了什么。

这不是一个快速或简单的过程,但天哪,它是有效的。突然之间,事情开始变得更有意义了。CSS 是一种奖励那些深入研究的语言。

大约一年前,我开始思考,也许我的经验可以帮助其他开发人员加快这个过程。毕竟,我们大多数人没有时间(或精力!)花数年时间深入研究文档和规范。

我辞去了 Gatsby Inc. 的一名软件工程师的工作,在过去的一年里,我一直专注于构建一个不同于其他任何东西的 CSS 课程。

它被称为JavaScript 开发人员CSS,它是一门综合的交互式课程,展示了 CSS 的真正工作原理。

有 200 多个课程,分布在 10 个模块中。并且您已经完成了其中之一:此阴影设计教程改编自课程!不过,在课程中,也有视频、练习和小游戏。

如果您发现 CSS 令人困惑或令人沮丧,我想帮助改变它。您可以在css-for-js.dev了解更多信息