想必写过 CSS 的同学都用过 box-shadow,它可以给元素设置阴影,增加立体效果。
比如说这样:

但它能做的可不只是阴影,还可以用来做出很多有趣的效果:
比如画蒙娜丽莎:

画星空:

这些效果都是 box-shadow 实现的!
是不是不敢相信?
今天我们就一起研究下 box-shadow 的高阶用法,来实现这些效果吧。
先过一下基础:
box-shadow 基础
box-shadow 可以设置 5 个值:x偏移量 y偏移量 阴影模糊半径 阴影扩散半径 阴影颜色
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
比如这个案例:

阴影中心点 x 轴偏移了 300px,y 轴偏移了 300px:

那阴影扩散半径是啥意思?
看这张图就明白了:

还有阴影模糊半径:

再来看下这几个值:
box-shadow: 300px 300px 30px 100px blue;
x 轴位移、y 轴位移都是指中心点的位移。阴影的半径就是 width/2 + 扩散半径 + 模糊半径。
而且 box-shadow 可以设置多个,通过逗号分隔,也就是多重阴影。
这样就可以用来做一些有意思的事情了:
比如把 width、height 设置为 0,然后设置多个阴影:

width、height 为 0,模糊半径为 0,扩散半径为 5px,那整个阴影就是一个 10px * 10px 的方块。
这样设置不同位置的 8 个阴影块就是上面的效果。
如果这样的块多了,是不是就类似像素块那样能展示图像了呢?
没错,蒙娜丽莎就是这么画出来的:
box-shadow 画蒙娜丽莎
整体思路上面已经分析出来了,就是通过 box-shadow 多重阴影设置每个像素块的颜色和位置:

这里 width、height 为 0,模糊半径为 0,扩散半径为 2px,那总体宽高就是 4px。
而每个块的中心点相距 5px,所以会留下一个间距。
我们试一下:

确实,通过这样一个个阴影块就能把蒙娜丽莎画出来。
然后我们把间距去掉,也就是把扩散半径设置大一点:

现在就连在一起了:

但这样看起来像素感太强了,我们给它加点模糊半径,比如设置个 4px:

这样好多了:

至此,神秘的蒙娜丽莎的微笑就完成了,只用到了 box-shadow 和一个 div!
这里是画了蒙娜丽莎,其实各种图片都能画,只要拿到像素数据就行,这个可以通过 canvas 的 getImageData 来拿到。
这里是一个个排列的阴影块,那如果随机打算这些阴影块,是不是可以做一些粒子效果呢?
比如星空。
我们来试一下:
box-shadow 画星空
星空大概是这样的:

画完蒙娜丽莎,我们知道了可以通过 box-shadow 多重阴影画出任意多个方块。不过那时是顺序排列的,现在我们希望把位置打乱,增加一些随机效果。
怎么随机呢?
css 里确实设置不了随机的东西,但是可以通过预处理器来做到,比如 sass。
我们通过 sass 来一个写循环生成随机 box-shadow 的函数:
@function multiple-box-shadow($n) {
$value: '#{random(2000)}px #{random(2000)}px #FFF';
@for $i from 2 through $n {
$value: '#{$value} , #{random(2000)}px #{random(2000)}px #FFF';
}
@return unquote($value);
}
这段代码是通过 @function 声明 sass 的函数,作用是传入 n,生成随机位置的白色 n 个阴影块。
声明了一个 $value 里,最后返回 $value,但要用 unquote 把引号去掉。
循环使用 @for $i from xx through yy 的语法,每次循环调用 random 函数生成 2000 内的随机整数。
这样就完成了 n 个随机位置的 box-shadow 的生成逻辑。
然后我们用一下它:
在 html 里放个 div:
<div id='stars'></div>
给它设置宽高和 box-shadow:
#stars {
width: 1px;
height: 1px;
box-shadow: multiple-box-shadow(700);
}
这里就没有设置扩散半径和模糊半径了,所以阴影块大小就是元素的宽高。
效果是这样的:

看下现在的 css:

确实有随机生成的 700 个 box-shadow 值。
这就是预处理器的作用。
当然,这种逻辑也可以用 JS 来写,运行时生成随机 box-shadow,但是渲染速度上会比 sass 编译期间生成的方案慢很多。
然后我们让它动起来,加上 animation:
#stars {
animation: animStar 50s linear infinite;
}
@keyframes animStar{
from{
transform: translateY(0px)
}
to {
transform: translateY(-2000px)
}
}
前面随机生成的 700 个星星的位置就是 0 到 2000px 的,所以这里是从 0 运动到 -2000px。
先把时间改短点,改成 3s 看下效果:

你会发现有段时间下面全是黑的,没有星星,这是为什么呢?
这个很容易想明白:当 translateY 快到 -2000px 的时候,剩下的部分星星不到一屏,其余的位置自然就没有星星了。
怎么解决这个问题呢?
其实这种还是比较经典的 CSS 问题,比如轮播图的无缝滚动也是同种原因。
解决方式就是在后面再接一个一模一样的,然后位移到了 -2000px 的时候,马上定位到 0 重新开始。这样就无缝了。
我们通过伪元素来设置这个:
$shadows-small: multiple-box-shadow(700);
#stars {
width: 1px;
height: 1px;
box-shadow: $shadows-small;
animation: animStar 3s linear infinite;
&:after {
content: " ";
position: absolute;
top: 2000px;
width: 1px;
height: 1px;
box-shadow: $shadows-small;
}
}
注意,这里要保证两次的 box-shadow 是一样的,所以通过一个变量来保存生成的值,两处都引用这个变量。
这样就无缝了:

但现在还是有点假,我们多加两种不同大小不同运动速度的星星:
当然,个数也不一样,越大的越少,分别生成 200 和 100个,动画时长分别设置 100s 和 150s:
<div id='stars2'></div>
<div id='stars3'></div>
$shadows-medium: multiple-box-shadow(200);
$shadows-big: multiple-box-shadow(100);
#stars2 {
width: 2px;
height: 2px;
box-shadow: $shadows-medium;
animation: animStar 100s linear infinite;
&:after {
content: " ";
position: absolute;
top: 2000px;
width: 2px;
height: 2px;
box-shadow: $shadows-medium;
}
}
#stars3 {
width: 3px;
height: 3px;
box-shadow: $shadows-big;
animation: animStar 150s linear infinite;
&:after {
content: " ";
position: absolute;
top: 2000px;
width: 3px;
height: 3px;
box-shadow: $shadows-big;
}
}
看下效果:

星空的感觉是不是就出来了!
不过现在的代码还有点不优雅,star 的样式重复写了 3 次,既然用了 sass,那可以把它抽成一个 mixin 来复用:
@mixin stars($size, $duration, $boxShadow) {
width: $size;
height: $size;
background: transparent;
box-shadow: $boxShadow;
animation: animStar $duration linear infinite;
&:after {
content: " ";
position: absolute;
top: 2000px;
width: $size;
height: $size;
background: transparent;
box-shadow: $shadows-small;
}
}
三处样式只要 include 这个 mixin,传入参数即可:
#stars {
@include stars(1px, 50s, $shadows-small);
}
#stars2 {
@include stars(2px, 100s, $shadows-medium);
}
#stars3 {
@include stars(3px, 150s, $shadows-big);
}
代码优雅了很多!
全部 scss 代码如下:
@function multiple-box-shadow($n) {
$value: '#{random(2000)}px #{random(2000)}px #FFF';
@for $i from 2 through $n {
$value: '#{$value} , #{random(2000)}px #{random(2000)}px #FFF';
}
@return unquote($value);
}
$shadows-small: multiple-box-shadow(700);
$shadows-medium: multiple-box-shadow(200);
$shadows-big: multiple-box-shadow(100);
html {
height: 100%;
background: #000;
overflow: hidden;
}
@mixin stars($size, $duration, $boxShadow) {
width: $size;
height: $size;
background: transparent;
box-shadow: $boxShadow;
animation: animStar $duration linear infinite;
&:after {
content: " ";
position: absolute;
top: 2000px;
width: $size;
height: $size;
background: transparent;
box-shadow: $shadows-small;
}
}
#stars {
@include stars(1px, 50s, $shadows-small);
}
#stars2 {
@include stars(2px, 100s, $shadows-medium);
}
#stars3 {
@include stars(3px, 150s, $shadows-big);
}
@keyframes animStar{
from{
transform: translateY(0px)
}
to {
transform: translateY(-2000px)
}
}
总结
box-shadow 我们一般用来做阴影,但其实也可以用来做一些有趣的效果。
阴影块的大小是由元素宽高、扩散半径、模糊半径这些决定的。
通过多重阴影顺序排列阴影块可以达到像素块的效果,画出蒙娜丽莎或者其他任意的图片。
也可以通过 sass 预处理器随机生成不同位置的阴影块来做出粒子效果,比如星空。
除了可以随机生成样式外,还可以通过 sass 的 mixin 来抽离相似的代码,多处复用,让 css 代码更优雅。这就是预处理器的意义。
box-shadow 的高阶玩法,你学会了么?
原文始发于微信公众号(神光的编程秘籍):box-shadow 高阶玩法:纯 CSS 画蒙娜丽莎和星空
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/108507.html