最近我在 CodePen 上看到了这样一个有意思的动画:
整个动画成果是在一个标签内,借助了 SVG PATH 实现。其外围在于对 突变(Gradient)的究极利用。
残缺的代码你能够看看这里 — CodePen DEMO — to the future 🍻 By Jane Ori]
源代码还是十分非常复杂的,并且叠加了简单的 SVG PATH 门路。
我尝试着将其略微拆分成几小块,使用不同的 CSS 高阶技巧从另外一个方面方向从新实现了一遍。因为整个过程还是有十分多有意思的 CSS 技巧,本文就给大家分享一下。
实现上半局部背景加落日
首先,咱们来实现上半局部的背景加落日成果:
<img width=”581″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187644739-3ea988df-6e78-4936-ad82-ecba5338d303.png”>
大家能够先进展思考下,这里让你来实现,会如何去做?须要多少个标签?
好的,这里,咱们利用一个 DOM 标签去实现这个图形:
<div class="g-bg"><div>
背景色好做,应用一个径向突变或者线性突变即可:
.g-bg {background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
}
如此,先实现一个背景:
<img width=”572″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187645547-c24578f4-aed5-4135-ac2a-0e09f57a1ab5.png”>
好,接下来,咱们应用其中一个 伪元素实现落日 的成果。
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
&::before {
content: "";
position: absolute;
bottom: 20%;
left: 10%;
right: 10%;
top: 10%;
background: radial-gradient(circle at 50% 100%, var(--color3), var(--color4) 55%, transparent 55.1%, transparent);
}
}
成果如下:
<img width=”585″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187646274-4f1f070a-9b42-4f80-ac65-e4e2fd886a0a.png”>
到这里,我感觉算是呈现了第一个技巧,也就是这一行代码 background: radial-gradient(circle at 50% 100%, var(--color3), var(--color4) 55%, transparent 55.1%, transparent)
,它用于在一个矩形元素中,通过径向突变从实色到通明色,实现一个半圆。
技巧 1:能够利用径向突变,在一个矩形 DIV 元素中,通过径向突变从实色到通明色的变动,实现一个半圆。
咱们持续,接下来,切割这个圆形,失去这样一种成果:
<img width=”851″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187648068-97115342-e98d-4da9-a274-a02a5ae630d4.png”>
留神,这里须要裁剪切割的中央不是红色,而是通明的,须要透出前面的背景色。
毫无疑问,这里须要应用 mask 来实现,咱们给伪元素加上 mask:
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
&::before {
content: "";
position: absolute;
bottom: 20%;
left: 10%;
right: 10%;
top: 10%;
background: radial-gradient(circle at 50% 100%, var(--color3), var(--color4) 55%, transparent 55.1%, transparent);
mask: linear-gradient(to top,
#000 0, #000 10%,
transparent 10%, transparent 13%,
#000 13%, #000 20%,
transparent 20%, transparent 22%,
#000 22%, #000 35%,
transparent 35%, transparent 36%,
#000 36%, #000 100%);
}
}
这样,咱们就实现了这个成果:
<img width=”529″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187648493-be115a64-c57f-4697-98c3-5eff4c042409.png”>
这里,引出了第二个技巧:
技巧 2:利用 mask 能够对图形进行裁剪,被裁剪区域将会变成通明。
好,接下来,咱们须要在整个图形上再叠加上竖形彩色条纹。这个其实也能够用 mask,如果整个图形前面还有一层彩色背景。
当然,这里咱们也能够把另外一个伪元素利用起来,利用它,通过多重线性突变(repeating-linear-gradient)实现这里的竖形彩色条纹。
看看代码:
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
&::before {
content: "";
// code of sun
}
&::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: repeating-linear-gradient(90deg, transparent 0, transparent 3px, rgba(0,0,0,.5) 4px, rgba(0,0,0,.5) 5px);
}
}
这里,咱们利用 repeating-linear-gradient
疾速创立批量的竖形彩色条纹成果,失去这样的成果:
<img width=”548″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187652524-f2aaba8e-08ba-427f-9144-52d07e6dc113.png”>
这里,失去技巧 3。
技巧 3:当你碰到大量反复有法则的线条,或者方块图形,你第一工夫就应该想到在一个 DOM 中利用突变而不是多个 DOM 去实现
好,至此,咱们整个上半局部就实现了。
利用 -webkit-box-reflect 实现倒影
有了下面的根底,接下来咱们要失去残缺的背景:
<img width=”577″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187654739-741fc449-9f95-4918-803a-3f896d7013bc.png”>
怎么做呢?换个配色从新实现一遍吗?当然不是,这里咱们利用 CSS 提供的倒影性能,能够疾速实现这个操作。
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
-webkit-box-reflect: below;
&::before {
content: "";
// ...
}
&::after {
content: "";
// ...
}
}
咱们给 .g-bg
加一个 -webkit-box-reflect: below
,意为下方的倒影:
<img width=”575″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187655575-acc52b7b-c75e-4861-8997-4f5546f8fd6a.png”>
尽管是复制了一个截然不同的 .g-bg
进去,然而和咱们要的成果还相差很多啊,怎么办呢?
别急,-webkit-box-reflect: below
还提供,倒影偏移间隔,倒影遮罩等属性。
咱们须要给下方的倒影,增加一个遮罩,批改一下 -webkit-box-reflect
的代码:
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
-webkit-box-reflect: below -50px linear-gradient(rgba(255, 255, 255, .2), transparent);
&::before {
content: "";
// ...
}
&::after {
content: "";
// ...
}
}
这样,咱们就失去了这样一种成果:
<img width=”596″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187661797-9035b246-1b9c-4e98-8589-1aa5b8f26251.png”>
这里,整个图形其实是半透明的,咱们在背地叠加上一层咱们想要的色调突变,能够利用 body 的伪元素:
body {
&::before {
position: absolute;
content: "";
top: 50%;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(var(--c5), var(--c6));
}
}
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
-webkit-box-reflect: below -50px linear-gradient(rgba(255, 255, 255, .2), transparent);
&::before {
content: "";
// ...
}
&::after {
content: "";
// ...
}
}
倒影通过半透明和背地的突变背景叠加,这样,咱们就完满实现了咱们想要的整体背景成果:
<img width=”577″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187654739-741fc449-9f95-4918-803a-3f896d7013bc.png”>
这里,咱们能够引出技巧 4。
技巧 4:当呈现反复的对称图形时,-webkit-box-reflect
兴许能派上用场。
利用 CSS 3D 动画实现线条动画
好,主体背景实现了,上面,咱们来试着实现 3D 线条动画:
利用 CSS 3D,咱们是能够实现这样一种成果的。咱们一步一步来拆解。
首先,咱们须要实现这样一种网格成果:
<img width=”525″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187662899-16bb8ac1-1f8b-40b9-a5ee-5b039fa40aa1.png”>
还记得下面的技巧 3 吗?当你碰到大量反复有法则的线条,或者方块图形,你第一工夫就应该想到在一个 DOM 中利用突变而不是多个 DOM 去实现。
这种成果,其实利用突变一个标签就组足够了:
<div class="grid"></div>
.grid {
background:
repeating-linear-gradient(var(--c1), var(--c1) 1px, transparent 1px, transparent 20px),
repeating-linear-gradient(90deg, var(--c1), var(--c1) 1px, transparent 1px, transparent 20px);
}
仅此而已,咱们就能失去一个网格图。
好的,接下来,须要利用 transform 让他出现一种 3D 视觉:
body {perspective: 300px;}
.grid {
position: absolute;
width: 300vw;
height: 600px;
left: -100vw;
top: 55vh;
transform-style: preserve-3d;
background:
repeating-linear-gradient(var(--c1), var(--c1) 1px, transparent 1px, transparent 20px),
repeating-linear-gradient(90deg, var(--c1), var(--c1) 1px, transparent 1px, transparent 20px);
transform: translate3d(0, 0, 0) rotateX(90deg);
transform-origin: 50% 0;
}
成果如下:
<img width=”739″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187663962-cbff25e5-89c1-42ec-a4c8-f2e337a396f9.png”>
因为,整体绕 X 轴旋转 90°,所以这里的 top: 55vh
很重要。
因为旋转圆心是 50% 0
,如果是 top: 50vh
,相当于整个图形会垂直于屏幕,如果 top
值小于 50vh,则整个网格是一种向上的翻转成果:
接着,咱们须要让其静止起来。
咱们尝试增加一个 translateZ 的静止动画:
.grid {
// ...
animation: move 10s infinite linear;
}
@keyframes move {
0% {transform: translate3d(0, 0, -600px) rotateX(90deg);
}
100% {transform: translate3d(0, 0, 600px) rotateX(90deg);
}
}
看看成果:
这里有个很重大的问题,仅仅只是单个动画,很难做到有限循环连接。
因而,咱们须要再加一组 Grid,动画两组动画先后登程,来实现整个动画的连接。
<div class="grid"></div>
<div class="grid"></div>
.grid {
// ...
animation: move 10s infinite linear;
}
.grid:nth-child(2) {animation: move 10s infinite -5s linear;}
@keyframes move {
0% {transform: translate3d(0, 0, -600px) rotateX(90deg);
}
100% {transform: translate3d(0, 0, 600px) rotateX(90deg);
}
}
咱们通过这么一种形式:
- 两组截然不同的动画,整个位移长度是 1200px,整个动画继续 10s,缓动为线性动画
- 第一组登程 5s 后(刚好前进了 600px),第二组再登程,如此 infinite 重复
- 整个 3D 动画,在近屏幕端看上去就是有限循环的一种成果
- 这里使用的是
-5s
,意思是提前 5s 登程,理论动画成果也就不会有期待感
如下(这里,为了录制 GIF,我整体是放慢了动画的速度):
能够看到,近屏幕端的动画是连续不断的,只是远端会呈现肯定的闪动。这里,能够失去技巧 5。
技巧 5:利用 2 组动画能够将一些无效在单组内的动画无奈实现的间断成果实现
这样,叠加上下面的成果,咱们就失去了这样一种成果:
能够看到,很靠近了。目前线条动画远处还有一些抖动。刚好,咱们还差一个山峰的成果,能够把这块瑕疵挡住。
应用 box-shadow 及 SVG 滤镜实现山脉成果
OK,最初,咱们在屏幕两头再叠加上一个山峰的成果就好。
这里,原成果应用的是一长串导出的 SVG 门路。如果咱们没有这种资源,只是想简略模仿一下成果。这里我给出一种可能可行的计划。
我首先利用一个圆角矩形进行旋转,再配合容器的 overflow: hidden
失去一个小山峰:
<div class="g-mountain"></div>
.g-mountain {
position: absolute;
left: 0;
right: 0;
top: 15%;
bottom: 42%;
overflow: hidden;
&::after {
content: "";
position: absolute;
top: 78%;
background: #011d3f;
width: 15vw;
height: 15vw;
transform: rotate(-45deg);
}
}
大略是这样一种成果:
<img width=”208″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187668374-e2efecd3-6ee5-47f1-98d9-f273123d6734.png”>
好,如果咱们想反复失去多个这样的图形,该怎么办呢?多个 DOM 吗?不是的,这里咱们能够利用 box-shadow
复制本身。
.g-mountain {
// ...
&::after {
content: "";
position: absolute;
top: 78%;
background: #011d3f;
width: 15vw;
height: 15vw;
transform: rotate(-45deg);
box-shadow:
-3vw -3vw, 5vw 5vw,
10vw 10vw 0 3vw, 15vw 20vw 0 4vw,
22vw 22vw 0 6vw, 25vw 30vw 0 12vw,
38vw 36vw 0 1vw, 41vw 39vw 0 3vw,
45vw 45vw 0 2vw, 52vw 52vw 0 4vh,
55vw 55vw 0 1.5vw, 61vw 61vw 0 0.5vw, 68vw 68vw 0 0;
}
}
这样,咱们就用一个标签,实现了一系列的“山”:
<img width=”832″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187669194-3dd2983a-bdf6-478a-9eb6-8a12d2d7630c.png”>
这里,咱们失去了技巧 6。
技巧 6:box-shadow
能够无效的复制本身,并且,能够利用第四个参数,扩散半径,来等比例放大本身。
其实,到这里,一个比拟毛糙的还原就实现了。当然,有一点小问题是,山峰显著不应该是一条条直线。是否营造出一种弯弯曲曲的外轮廓成果呢?
这个应用纯 CSS 是比拟难实现的,当然,好在这里咱们能够使用上之前给大家屡次提及过的 SVG 滤镜。
利用 feTurbulence
能够无效实现一些波形纹理成果。并且能够通过 CSS filter 疾速引入。
<div class="g-mountain"></div>
<svg width="0">
<filter id="filter">
<feTurbulence id="turbulence" type="fractalNoise" baseFrequency=".03" numOctaves="20" />
<feDisplacementMap in="SourceGraphic" scale="30" />
</filter>
</svg>
.g-mountain {
// ...
filter: url('#filter');
&::after {}}
这里,本来,整齐划一的直线,立马变得横七竖八了起来,看起来更像是山脉的轮廓:
<img width=”801″ alt=”image” src=”https://user-images.githubusercontent.com/8554143/187670726-b262c845-51b3-41e2-a49d-61bc66060228.png”>
这里,咱们得出了技巧 7。
技巧 7:SVG 滤镜能够通过 CSS 滤镜疾速引入,SVG 滤镜能够实现一些 CSS 实现不了的事件,譬如一些非凡的纹理,波纹,烟雾颗粒感等等成果。
好,至此,咱们就大体上依照本人的了解,从新实现了一遍上述的动画,再做一些简略的润饰,最终的成果如下:
CodePen Demo — Pure CSS to the future
最初
明天的内容有点多,技巧也很猛。文中所有技巧在我过往的文章中都有十分高频的呈现次数,对其中细节不理解的能够在 iCSS 中通过关键字查找,好好补一补。
好了,本文到此结束,心愿本文对你有所帮忙 :)
更多精彩 CSS 技术文章汇总在我的 Github — iCSS,继续更新,欢送点个 star 订阅珍藏。
如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。