乐趣区

关于前端:高阶-CSS-技巧在复杂动效中的应用

最近我在 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);
    }
}

咱们通过这么一种形式:

  1. 两组截然不同的动画,整个位移长度是 1200px,整个动画继续 10s,缓动为线性动画
  2. 第一组登程 5s 后(刚好前进了 600px),第二组再登程,如此 infinite 重复
  3. 整个 3D 动画,在近屏幕端看上去就是有限循环的一种成果
  4. 这里使用的是 -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 订阅珍藏。

如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。

退出移动版