起因
最近负责公司的一些代码效果优化工作,碰到了一个网格动画变色的需求,之前的开发同学觉得这个效果比较麻烦不好实现,当时是搁置下来了,现在我仔细思索之后发现实现该效果的方法有很多,而且都不需要JS
的参与!故此写篇文章分享给大家~
根据图中的效果我们可以先拆解一下需要实现的效果
- 这个网格是由重复的横线和竖线组合而成
- 网格线段颜色会随着线段蔓延从透明过度到橘色
- 网格蔓延时越靠近底部的区域起点越大
如何实现一个网格
实现一个网格前我们要仔细观察需求,能发现效果图上横线和竖线分别是独立运行的,这也代表我们这个网格形状的实现也要分别实现横线和竖线!
我们都知道在CSS
中需要实现重复背景时第一个考虑的属性就应该是background
.hor {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom, orange,transparent);
}
此时我们得到的是一个渐变的背景~,如果我们调整一下渐变的距离!
.hor {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom, orange 10%, transparent 10%);
}
如果盒子高度100 * 100
,我们就得到了一个10px
橘色,90px
的透明色背景,此时转换一下思路,如果盒子高度20 * 20
我们就能得到一个高度2px
的橘色线条!不断重复这个橘色线条就可以绘制出一个网格线!在不能改变盒子高度的情况下我们只需要用background-size
来改变背景色的高度~
.hor {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom, orange 10%, transparent 10%);
background-size: 2px 20px;
}
成功完成横向后,竖线我们也依葫芦画瓢~
.hor {
width: 100%;
height: 100%;
position: relative;
background-image: linear-gradient(to bottom, orange 10%, transparent 10%);
background-size: 2px 20px;
}
.hor:before{
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(to right, orange 10%, transparent 10%);
background-size: 20px 20px;
}
网格如何渐变过渡
完成了一个网格之后我们就需要考虑颜色渐变的问题了,background-image
本身是不支持过渡效果的,这也是这个效果实现的难点之一!
此时我们应该转变一下思路background-image
不支持过渡,background
却支持过渡,如果可以在保留网格的情况把background
的颜色映射到网格上就好了!此时就需要我们的混合模式出场了!
解法一 mix-blend-mode
混合模式本身的效果非常多,我们这次用到的是darken
属性,故名思义这个混合模式是把图层交汇处更暗的图像显示出来,而白色本身是最亮的颜色!所以如果我们把底色改成橘色到黑色的渐变,网格改成白色网格,则代表着底部的橘色背景会被混合到外层的网格上来!
可以看到效果确实实现了,但是这个方案有一个弊端就是要配合mix-blend-mode: darken;
所以底色要求比较高,下面介绍的方案二是既兼顾了需求又十分方便!
解法二 @property
关于@property
的用法可以点击CSS @property,让不可能变可能
我们这里的解法是既然你不支持直接的background-image
过渡效果,那么我就使用@property
让渐变色强行支持过渡!
@property --color {
syntax: '<color>';
inherits: false;
initial-value: transparent;
}
.hor {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom, var(--color) 10%, transparent 10%);
background-size: 2px 20px;
background-repeat: repeat;
position: relative;
animation: hor 2s ease-in-out infinite alternate;
}
@keyframes hor {
0% {
--color: transparent;
}
100% {
--color: orange;
}
}
但是需要注意的是@property
至今仍是被标记成实验性语法,兼容性如下
那么有没有兼容性又好又灵活的办法呢?有,请看解法三!
解法三 svg
上述两个方案我们都是使用了CSS
属性来完成的,但我们如果换个思路会发现,使用svg
完成这个效果更加的容易!
首先我们还是需要绘制出来一个网格,此时就需要svg
中的pattern
的标签!
SVG 的
pattern
元素用于定义可重复的图案或背景,可以在其他图形元素(如矩形、圆形、路径等)中应用这个图案。pattern
是一个非常强大的工具,可以用来创建复杂的背景、填充样式,以及具有高度自定义的重复图案。
举个栗子!
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="squarePattern" width="100%" height="20" patternUnits="userSpaceOnUse">
<line x1="0" x2="100%" y1="0" y2="0" stroke="#1677ff" strokeWidth="1"></line>
</pattern>
<pattern id="squarePattern2" width="20" height="100%" patternUnits="userSpaceOnUse">
<line x1="0" x2="0" y1="0" y2="100%" stroke="#1677ff" strokeWidth="1"></line>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#squarePattern)" />
<rect width="100%" height="100%" fill="url(#squarePattern2)" />
如代码所示我们用了两个pattern
分别重复了横线和竖线形成了网格的样子,至于颜色变化在svg
中就更加简单了!只需要使用animate
标签,并设置开始颜色和结束的颜色即可。
<animate
dur="5s"
to="orange"
fill='freeze'
repeatCount="1"
from='transparent'
attributeName="stroke"/>
至此关于网格颜色变化我们已经有三种方案!那么该进入我们的下一步
网格如何蔓延
到了最后一步其实反而非常简单了,我们只需要切割掉这个网格并不断变化切割的路径即可,使用mask
或者clip-path
都可以!
这里着重写一下关于svg
使用clip-path
遇到的问题。关于svg
的clipPath
我们要明白一个点是,clipPath
默认是不支持百分比单位的
<clipPath id="clipPath">
<polygon id="polygon" points="0 0, 0 100%,30% 100%, 0 0"/>
</clipPath>
上述这种写法是不生效的,因为本身clipPath
中的坐标是基于整个 SVG
画布的绝对坐标来计算的,所以我们需要调整clipPathUnits
来改变默认行为!
clipPathUnits
有两个主要取值:
userSpaceOnUse
(默认值)objectBoundingBox
1. userSpaceOnUse
当 clipPathUnits
设置为 userSpaceOnUse
时,clipPath
内部元素的坐标系统与整个 SVG 文档的用户坐标系统相同。也就是说,裁剪区域的坐标是基于整个 SVG 画布的绝对坐标来计算的。
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip1" clipPathUnits="userSpaceOnUse">
<rect x="50" y="50" width="100" height="100" />
</clipPath>
</defs>
<circle cx="100" cy="100" r="100" clip-path="url(#clip1)" fill="blue" />
</svg>
clipPathUnits="userSpaceOnUse"
表示裁剪矩形的坐标(x=50, y=50)
是相对于整个 SVG 画布的坐标系。- 结果是只有圆形的中间部分(被矩形框包含的部分)会显示出来。
2. objectBoundingBox
当 clipPathUnits
设置为 objectBoundingBox
时,clipPath
内部元素的坐标系统是相对于被裁剪元素的边界框 (bounding box
) 来计算的。这意味着坐标是相对的,取值范围通常在 [0, 1]
之间,表示边界框的比例。
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip2" clipPathUnits="objectBoundingBox">
<rect x="0.25" y="0.25" width="0.5" height="0.5" />
</clipPath>
</defs>
<circle cx="100" cy="100" r="100" clip-path="url(#clip2)" fill="blue" />
</svg>
-
clipPathUnits="objectBoundingBox"
表示裁剪矩形的坐标(x=0.25, y=0.25)
是相对于圆形元素边界框的 25% 位置,宽度和高度各为圆形边界框的 50%。 -
结果是只有圆形中间部分被显示出来,但裁剪区域是根据圆形自身的尺寸动态调整的。
看了上面两个栗子后,我们就应该知道怎么做啦!
结语
一个网格渐变动画我们就能用最少三种实现方式来实现,CSS
的趣味性实在令人着迷!