CSS揭秘之视觉效果

385 阅读5分钟

单侧投影

box-shadow: 2px 3px 4px rgba(0,0,0,.5);

box-shadow绘制原理:

  1. 以该元素相同的尺寸和位置,画一个 rgba(0,0,0,.5) 的矩形;
  2. 把它向右移 2px ,向下移 3px;
  3. 使用高斯模糊算法(或类似算法)将它进行 4px 的模糊处理。这在本质上表示在阴影边缘发生阴影色和纯透明色之间的颜色过渡长度近似于模糊半径的两倍(比如在这里是 8px );
  4. 接下来,模糊后的矩形与原始元素的交集部分会被切除掉,因此它看起来像是在该元素的“后面”。这跟大多数开发者所理解的情况(元素叠 除非特别注明,元素的尺寸表示它的 border box 的尺寸,而不是它的 CSS 宽度和高度;
  5. 如果我们想要的投影只能出现在单侧(而不是相邻的两侧),最终的解决方案来自 box-shadow 鲜为人知的第四个长度参数。它排在模糊半径参数之后,称作扩张半径。这个参数会根据你指定的值去扩大或(当指定负值时)缩小投影的尺寸。举例来说,一个 -5px 的扩张半径会把投 影的宽度和高度各减少 10px (即每边各 5px )。从逻辑上来说,如果我们应用一个负的扩张半径,而它的值刚好等于模糊半径,那么投影的尺寸就会与投影所属元素的尺寸完全一致。除非用偏移量(前两个长度参数)来移动它,我们将完全看不见任何投影。因此,如果给投影应用一个正的垂直偏移量,我们就会在元素的底部看到一道投影,而元素的另外三侧是没有投影的,这正是我们一直苦苦追寻的效果
box-shadow: 0 5px 4px -4px black;

![](p6-juejin.byteimg.com/tos-cn-i-k3…

双侧投影

当我们想把投影设置在元素的两条对边(比如左侧和右侧)时,事情就变得棘手了。因为扩张半径在四个方向上的作用是均等的(也就是说,我们无法指定投影在水平方向上放大,而在垂直方向上缩小),唯一的办法是用两块投影(每边各一块)来达到目的。然后基本上就是把“单侧投影”中的技巧运用两次:

box-shadow: 5px 0 5px -5px black,-5px 0 5px -5px black;

不规则投影

难题

当我们想给一个矩形或其他能用 border-radius 生成的形状(在“自适应的椭圆”一节中可以看到一些示例)加投影时, box-shadow 的表现都堪称完美。但是,当元素添加了一些伪元素或半透明的装饰之后,它就有些力不从心了,因为 border-radius 会无耻地忽视透明部分。这类情况包括:

  1. 半透明图像、背景图像、或者 border-image (比如老式的金质像框);
  2. 元素设置了点状、虚线或半透明的边框,但没有背景(或者当background-clip 不是 border-box 时);
  3. 对话气泡,它的小尾巴通常是用伪元素生成的;
  4. 我们在“切角效果”一节中见过的切角形状;
  5. 几乎所有的折角效果,包括“折角效果”一节将提到的例子;
  6. 通过 clip-path 生成的形状,比如“菱形图片”一节中提到的菱形图像。

解决方案

滤镜效果规范(w3.org/TR/filter-e… )为这个问题提供了一个解决方案。它引入了一个叫作 filter 的新属性,这个属性也是从 SVG 那里借鉴过来的。尽管 CSS 滤镜基本上就是 SVG 滤镜,但我们并不需要掌握任何SVG 知识。相反,只需要一些函数就可以很方便地指定滤镜效果了,比如blur() 、 grayscale() 以及我们需要的 drop-shadow() !如果你喜欢,甚至可以把多个滤镜串连起来,只要用空格把它们分隔开就可以了,比如:

filter: blur() grayscale() drop-shadow();
border:5px dashed red;
filter: drop-shadow(5px 5px 5px rgba(0,0,0,.5));

染色效果

难题

  1. 为一幅灰度图片(或是被转换为灰度模式的彩色图片)增加染色效果(color tint),是一种流行且优雅的方式,可以给一系列风格迥异的照片带来视觉上的一致性。我们通常会在静止状态下应用这个效果,当发生 :hover或其他交互时再去除。
  2. 一直以来,我们需要使用图像处理软件来生成图片的两个版本,然后再写一些简单的 CSS 代码来处理这两个版本的交替显现。这个方法行得通,但它会导致更大的文件体积和额外的 HTTP 请求,而且在可维护性方面也是一场噩梦。想像一下,一旦我们决定改变这个效果的主色调,就不得不处理所有的图片,为它们重新制作全套的单色版本!
  3. 另外一种方法是:在图片的上层覆盖一层半透明的纯色;或者把图片设置为半透明并覆盖在一层实色背景之上。但这其实并不是真正的染色效果:不仅没有把图片中的各种颜色转换为目标色调,同时也极大地削弱了图片的对比度。此外还有基于 JavaScript 的方案,把图片置入 canvas 元素中,并利用脚本对其进行染色处理。这确实可以得到真实的染色效果,但性能不佳,而且限制很多。
  4. 难道就没有一种更简单的、纯 CSS 的方法能实现图片染色效果吗?

基于滤镜的方案

img {
    transition: .5s filter;
    filter: sepia(1) saturate(4) hue-rotate(295deg);
}
img:hover,img:focus {
    filter: none;
}

hover效果: