这是我参与更文挑战的第7天,活动详情查看: 更文挑战
单侧投影
难题
box-shadow
如何在元素的一侧(偶尔是两侧)设置投影。
单侧投影
大多数人使用 box-shadow 的方法是,指定三个长度值和一个颜色值:
box-shadow: 2px 3px 4px rgba(0,0,0,.5);
复制代码
box-shadow 的绘制原理:
- 以该元素相同的尺寸和位置,画一个 rgba(0,0,0,.5) 的矩形;
- 把它向右移 2px,向下移 3px;
- 使用模糊算法将它进行 4px 的模糊处理;
- 将模糊后的矩形与原始元素的交集部分会被切除掉。
使用 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);
复制代码
此时,就把图片的色调改变了,如果这个效果需要由 :hover 或其他状态来触发切换,还可以为这个变化增加过渡动画:
img {
transition: .5s filter;
filter: sepia(1) saturate(4) hue-rotate(295deg);
}
img:hover,
img:focus {
filter: none;
}
复制代码
基于混合模式的方案
对一个元素设置混合模式,有两个属性可以使用:mix-blend-mode
可以为整个元素设置混合模式,background-blend-mode
可以为每层背景单独指定混合模式。因此有两种:
- 把图片包裹在一个容器中,并把容器的背景色设置为想要的主色调。
- 不用图片元素,而是用
<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)">
复制代码
上述两种方法可以实现滤镜,但是都不够理想:
- 图片的尺寸需要在 CSS 代码中写死;
- 在语义上,这个元素并不是一张图片,因此并不会被读屏器之类的设备读出来。
最后说一句
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。