阅读 222

css揭秘 - 视觉效果(一)

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

单侧投影

难题

box-shadow 如何在元素的一侧(偶尔是两侧)设置投影。

单侧投影

大多数人使用 box-shadow 的方法是,指定三个长度值和一个颜色值:

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

box-shadow 的绘制原理:

  1. 以该元素相同的尺寸和位置,画一个 rgba(0,0,0,.5) 的矩形;
  2. 把它向右移 2px,向下移 3px;
  3. 使用模糊算法将它进行 4px 的模糊处理;
  4. 将模糊后的矩形与原始元素的交集部分会被切除掉。

使用 4px 的模糊半径意味着投影的尺寸会比元素本身的尺寸大约 8px ,因此投影的最外圈会从元素的四面向外显露出来,只需改变偏移量,就可以把投影的顶部和左侧隐藏起来,只要这两个方向上的偏移量不小于4px 就可以了。但是,这在某种程度上会导致外露的投影太过浓重,看起来不是很美观。另外,就算这个问题勉强可以接受,但跟想要的投影在单侧的不相符。

最终的解决方案来自 box-shadow 的第四个长度参数,排在模糊半径参数的后面,称作扩张半径。这个参数会根据指定的值去扩大(当指定值为负数)缩小投影的尺寸。 举例来说:一个 -5px 的扩张半径会把投影的宽度和高度各减少 10px。因此,下面代码即为想要的效果:

box-shadow: 0 5px 4px -4px black;
box-shadow: 5px 0 4px -4px black;
box-shadow: 0 -5px 4px -4px black;
box-shadow: -5px 0 4px -4px black;
复制代码

邻边投影

box-shadow: 2px 3px 4px rgba(0,0,0,.5);
box-shadow: 3px 3px 6px -3px black;
复制代码

双侧投影

因为扩张半径在四个方向上的作用是相等的(即无法指定投影在水平方向上放大,而在垂直方向上缩小),唯一的办法是用两块投影(每边各一块)来达到目的。

box-shadow: 5px 0 5px -5px black,
           -5px 0 5px -5px black;
复制代码

不规则投影

难题

给一个矩形或者其他能用 border-radius 生成的形状加投影时,box-shadow 的表现都堪称完美。但是当元素添加了一些伪元素或者半透明的装饰后,box-shadow 就有点力不从心了。这类情况包括:

  • 半透明图像、背景图像、或者 border-image;
  • 元素设置了点状、虚线或半透明的边框,但没有背景;
  • 对话气泡,它的小尾巴通常是用伪元素生成的;
  • 在“切角效果”一节中见过的切角形状;
  • 几乎所有的折角效果,包括“折角效果”一节将提到的例子;
  • 通过 clip-path 生成的形状,比如“菱形图片”一节中提到的菱形图像。
  • 如果对这些元素使用 box-shadow 则会得到如下所示的效果。

解决方案

滤镜效果规范为这个问题提供了一个解决方案,它引入了一个叫作 filter 的新属性,比如blur()grayscale() 以及我们需要的 drop-shadow()

drop-shadow() 滤镜可接受的参数基本上跟 box-shadow 属性是一样的,但不包括扩张半径,不包括 inset 关键字,也不支持逗号分割的多层投影语法。举个例子,上面的投影:

box-shadow: 2px 2px 10px rgba(0,0,0,.5);
复制代码

可以这样写:

filter: drop-shadow(2px 2px 10px rgba(0,0,0,.5));
复制代码

染色效果

难题

为一幅灰度图片(或是被转换为灰度模式的彩色图片)增加染色效果(color tint),是一种流行且优雅的方式,可以给一系列风格迥异的照片带来视觉上的一致性。我们通常会在静止状态下应用这个效果,当发生 :hover 或其他交互时再去除。最初的解决方法是用不同的图片来做,这样不仅会增加 HTTP 的请求,同时一旦色系发生变化,则又需要重新作图,成本很高。

另外一种方法是:在图片的上层覆盖一层半透明的纯色;或者把图片设置为半透明并覆盖在一层实色背景之上。但这并不是真正的染色效果。

此外还有基于 JavaScript 的方案,把图片置入 <canvas> 元素中,并利用脚本对其进行染色处理。但是用这种方案限制很多。

基于滤镜的方案

没有现成的滤镜可以实现这个效果,需要将多个滤镜进行组合。

  • sepia() :给图片增加一种降饱和度的橙黄色染色效果
  • saturate() :给每个像素提升饱和度
  • hue-rotate() :把每个像素的色相以指定的度数进行偏移
filter: sepia(1);
filter: sepia(1) saturate(4);
filter: sepia(1) saturate(4) hue-rotate(295deg);
复制代码

image.png 此时,就把图片的色调改变了,如果这个效果需要由 :hover 或其他状态来触发切换,还可以为这个变化增加过渡动画:

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

img:hover,
img:focus {
    filter: none;
}
复制代码

3.gif

基于混合模式的方案

对一个元素设置混合模式,有两个属性可以使用:mix-blend-mode 可以为整个元素设置混合模式,background-blend-mode 可以为每层背景单独指定混合模式。因此有两种:

  1. 把图片包裹在一个容器中,并把容器的背景色设置为想要的主色调。
  2. 不用图片元素,而是用 <div> 元素——把这个元素的第一层背景设置为要染色的图片,并把第二层的背景设置为想要的主色调。
<style>
a{ background: hsl(335, 100%, 50%); }
img{
    mix-blend-mode: luminosity;
    display: inline-block;
}
</style>
<a href="#"><img src="1.jpg" /></a>
复制代码

滤镜是可动画的,而混合模式则不是!

<style>
.img6 {
    width: 100px;
    height: 100px;
    background-size: cover;
    background-color: hsl(335, 100%, 50%);
    background-blend-mode: luminosity;
    transition: .5s background-color;
}

.img6:hover {
    background-color: transparent;
}
</style>
<div class="img6" style="background-image:url(2.jpg)">
复制代码

上述两种方法可以实现滤镜,但是都不够理想:

  1. 图片的尺寸需要在 CSS 代码中写死;
  2. 在语义上,这个元素并不是一张图片,因此并不会被读屏器之类的设备读出来。

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。

文章分类
前端
文章标签