开福门,画真龙,画龙点睛庆龙年

龙年到了,今天用上所学的技术,画出一条龙,庆祝龙年到来。

废话不多说,直接体验效果先:

;

体验完毕就来讲讲一些实现的细节。

1. 布局

整个效果是由三个部分组成的,分别是福字、门楼、龙。

这三个部分都是有一个外层的容器,然后在容器中放置对应的元素,每个容器的高度都是100vh,然后内容居中。

1.1. 福字

开福门,画真龙,画龙点睛庆龙年

福字是整个效果中的最简单的部分,先从html结构开始:

<div class="fu-container">
    <div class="fu"></div>
</div>

.fu-container就是上面说的外层容器,.fu就是福字的内容。

可以看到的我的福字边框会有一些半圆来装饰,这个是通过css的渐变来实现的,具体代码如下:

.fu {
  background-image: linear-gradient(#d7000d, #d7000d),
  radial-gradient(#ffb53d 10px, transparent 10px);
  background-size: calc(100% - 20px) calc(100% - 20px), 20px 20px;
  background-position: 10px 10px, 0 0;
  background-repeat: no-repeat, repeat;
}

首先使用background-image来添加两个渐变,注意哈,background-image的背景叠加是左边的会叠加到右边的上面;

也就是说,我上面的radial-gradient(#ffb53d 10px, transparent 10px)就是修饰边框的,然后linear-gradient(#d7000d, #d7000d)就是福字的红色背景色。

接着就是设置background-sizebackground-size也可以设置多个值,对应的也是background-image的顺序;

我这里设置的是calc(100% - 20px) calc(100% - 20px),第一个值是福字的背景色,用calc来减去20px这样就就可以让边框的半圆显示出来;

第二个值是边框的半圆,设置为20px 20px,这样就代表radial-gradient只会创建一个20px 20px大小的圆,然后重复平铺;

可以看一下去掉红色背景的效果,只有一堆小圆平铺到了整个容器中,然后我们再加上红色背景,就会可以只剩下边框的半圆了。

开福门,画真龙,画龙点睛庆龙年

然后设置background-position,这个就是设置渐变背景的位置,同background-size一样,也是对应background-image的顺序;

我们只需要将红色背景的位置设置为10px 10px,这样就可以挡住半个圆了,从而实现边框的效果。

最后就是设置background-repeat,这个就是设置背景的重复方式,同样也是对应background-image的顺序,这个大家应该都熟悉。

然后就是让其变成一个菱形,我只需要将容器旋转45度,然后再将福字旋转-45度,就可以实现现在这个效果了。

.fu {
    /* 省略其他样式 */
  transform: rotate(45deg);
}

.fu::before {
  content: '福';
  font-size: 100px;
  color: #fff;
  transform: rotate(-45deg);
}

最后在加上动画,一个不停旋转的福字就出来了。

.fu {
  /* 省略其他样式 */
  animation: fu-animate 5s linear infinite;
}

@keyframes fu-animate {
  0% {
    transform: translateY(-10px) rotate3d(0, 1, 0, 0deg) rotate(45deg);
  }
  50% {
    transform: translateY(10px) rotate3d(0, 1, 0, 180deg) rotate(45deg);
  }
  100% {
    transform: translateY(-10px) rotate3d(0, 1, 0, 360deg) rotate(45deg);
  }
}

开福门,画真龙,画龙点睛庆龙年

1.2. 门楼

开福门,画真龙,画龙点睛庆龙年

门楼主要分为三个部分,额房、门柱、灯笼,这些也都是使用css渐变来实现的,先来看一下html结构

<div class="girder">
    <div class="e-fang">
        <div class="e-fang__top"></div>
    </div>
    <div class="e-fang-2">
        <div class="e-fang-2__top"></div>
    </div>

    <div class="plaque">
        <div class="hengpi" style="opacity: 0; transition: opacity .5s; transition-delay: 2s;">岁岁平安</div>
        <div class="lantern" v-for="i in 3">
            <div class="lantern-inner"></div>
            <div class="lantern-inner"></div>
            <div class="lantern-inner"></div>
            <div class="lantern-inner"></div>
            <div class="lantern-inner"></div>
        </div>
    </div>

    <div class="column left"></div>
    <div class="column right"></div>
</div>

看着代码很多,其实css更多,先来看一下额房的样式:

/* 省略很多代码,只留关键代码 */
.e-fang::before {
    background-image: radial-gradient(200px 200px at -30px -138px, white 190px, transparent 190px),
    radial-gradient(200px 200px at 530px -138px, white 190px, transparent 190px),
    repeating-linear-gradient(90deg, #d7000d, #333 10px);
    clip-path: polygon(20% 0%, 80% 0, 100% 100%, 0% 100%);
}

首先我用渐变背景创建了如下图的一个条纹效果,这里使用的是repeating-linear-gradient(90deg, #d7000d, #333 10px)这一个规则:

开福门,画真龙,画龙点睛庆龙年

接着我使用clip-path来裁剪出一个梯形,这样就可以看类似一个屋顶的效果了:

开福门,画真龙,画龙点睛庆龙年

但是这样还不够,因为左右两边太尖锐了,我用两个非常大的圆来遮挡住,这样看起来就会有一个平滑的弧度效果:

开福门,画真龙,画龙点睛庆龙年

最后在加上顶部的一个横柱,底部的一个瓦片下垂的效果,完整的效果就出来了:

开福门,画真龙,画龙点睛庆龙年

额房有两层,第二层的额房和第一层的额房差不多,样式只是稍作修改,其他的样式感兴趣的可以去f12自己看看,这里就不过多的讲解了;

然后就是门柱,门柱就非常简单了,直接两个div,然后设置一下渐变背景色就好了:

.column {
  position: absolute;
  top: 140px;
  width: 100px;
  height: 600px;
  background: linear-gradient(90deg, #333, #d7000d, #333);
}

最后就是灯笼了,灯笼是使用一个css3d效果来实现的,首先是一个div,然后设置transform-style: preserve-3d来开启3d效果,然后设置perspective来设置观察者的位置,这样只需要旋转内部的div就可以看到灯笼的轴线效果了。

.lantern {
  margin-top: 20px;
  position: relative;
  height: 100px;
  width: 120px;
  background: #d7000d;
  border-radius: 50%;
  transform-style: preserve-3d;
  perspective: 300px;
}

.lantern-inner {
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: yellow 2px solid;
  box-sizing: border-box;
}

.lantern-inner:nth-child(1) {
  transform: rotateY(0deg);
}

.lantern-inner:nth-child(2) {
  transform: rotateY(50deg);
}

.lantern-inner:nth-child(3) {
  transform: rotateY(70deg);
}

.lantern-inner:nth-child(4) {
  transform: rotateY(110deg);
}

.lantern-inner:nth-child(5) {
  transform: rotateY(130deg);
}

/* 省略灯笼顶部和底部的样式 */

这里内部会有五个div,然后设置transform来旋转,一个灯笼就出来了,效果如下:

开福门,画真龙,画龙点睛庆龙年

1.3. 龙

开福门,画真龙,画龙点睛庆龙年

整条龙就是一个svg,这里我只设了svg的描边,填充色是透明的,看到的效果就是一个初稿的龙,这样我就可以做一个描边动画了。

2. 动画

2.1. 福字

其实福字并没有动画效果,福字除了最开始的旋转动画,就完全没有动画了。

福字的滚动居中效果其实是通过position: sticky来实现的,这样就可以让福字黏在页面中间:

.fu-container {
  position: sticky;
  top: 0;
  left: 0;
}

2.2. 门楼

开福门,画真龙,画龙点睛庆龙年

门楼会伴随着滚动透明度和大小会发生变化,看起来就像是从底部慢慢升起来一样,这个效果是通过js来实现的,监听scroll事件,然后根据滚动的距离来设置透明度和大小。

const girder = document.querySelector('.girder');
window.addEventListener('scroll', () => {
    const vh = window.innerHeight;
    const scroll = window.scrollY;
    girder.style.opacity = 1 - (vh - scroll) / vh;

    let scale = 1 - (vh - scroll) / vh;
    scale = scale > 1 ? 1 : scale;
    girder.style.transform = `scale(${scale})`;
})

这里就是监听scroll事件,然后根据滚动的距离来设置透明度和大小,这样就可以实现门楼的滚动效果了。

因为门楼本身就在下面,往上滚动的时候就会慢慢显示出来,所以看起来就像是从底部慢慢升起来一样。

2.3. 贴春联

开福门,画真龙,画龙点睛庆龙年

过年当然少不了贴春联的环节,门楼都出来了,肯定是需要有春联的;

当滚动到底部之后,福字的旋转动画会停止,然后点击福字,福字消失,春联出现;

这个效果当然还是得靠js

if (scale >= 1 && fu.style.animation !== 'unset') {
    fu.style.animation = 'unset'

    fu.addEventListener('click', () => {
        const hengpi = document.querySelector('.hengpi');
        const couplets = document.querySelectorAll('.couplets');

        couplets[0].style.opacity = 1;
        couplets[1].style.opacity = 1;
        hengpi.style.opacity = 1;

        fu.style.opacity = 0;
    });
}

我通过判断scale是否为1来确认是否滚动到了底部,如果到了底部就取消福字的旋转动画,然后给福字添加一个点击事件;

春联是提前就定位好了的,也提前设置好了transition,所以我只需要设置opacity就可以实现春联的出现效果了。

2.4. 画龙

当春联贴完之后,我会将龙显示出来,所以上面的动图可以看到,春联贴上了之后,鼠标会有一个和龙互动的hover效果;

龙的显示是在春联贴上了之后,延时3s显示的:

setTimeout(() => {
    const fuContainer = document.querySelector('.fu-container');
    fuContainer.style.display = 'none';
    loong.style.display = 'flex';
    container.style.height = '300vh';
    stepAnimation = 2;
}, 2000);

接着就是滚动会出现龙的描边动画,效果如下:

开福门,画真龙,画龙点睛庆龙年

代码如下:

const vh = window.innerHeight;
const scroll = window.scrollY - vh;
let scale = 1 - (vh - scroll) / vh;
scale = scale > 1 ? 1 : scale;

girder.style.transform = `scale(${1 + scale})`;
girder.style.opacity = 1 - scale;

paths.forEach((path, index) => {
    const length = path.getTotalLength();
    path.style.strokeDashoffset = length * (1 - scale);
});

这里其实和门楼的滚动效果差不多,门楼的透明度是从1 - 0的一个过程,这一点我们可以理解为从0% - 100%,然后对应的svg的描边差乘以这个百分比值,就可以看到一个通过滚动距离来控制描边的效果了。

2.5. 画龙点睛

可以看到最后龙出现了之后,左下角会有几个颜色选择的色块,这里我们是可以为龙进行填色的:

开福门,画真龙,画龙点睛庆龙年

这一块就是对每个path添加一个click事件,然后根据点击的颜色来设置fill的颜色:

paths.forEach((path, index) => {
    path.addEventListener('click', () => {
        path.style.fill = colors[index];
    });
});

这个没啥好说的,最后就是画龙点睛的效果了,当所有的path都填色之后,会有一个消息提示,让点击龙的眼睛(其实是龙头的那一块),然后龙就会活过来:

开福门,画真龙,画龙点睛庆龙年

可以看看最终的效果,龙的翎毛和龙爪部分会有一个类似呼吸的效果,这一块是通过svgfilter来实现的,具体代码如下:

<svg style="display:none;">
    <defs>
        <filter id="filter" x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox"
                primitiveUnits="userSpaceOnUse" color-interpolation-filters="linearRGB">
            <feTurbulence id="feTurbulence" type="fractalNoise" baseFrequency="0 0" numOctaves="20" seed="10"
                          stitchTiles="stitch" x="0%" y="0%" width="100%" height="100%" result="turbulence"/>
            <feDisplacementMap in="SourceGraphic" in2="turbulence" scale="10" xChannelSelector="R" yChannelSelector="B"
                               x="0%" y="0%" width="100%" height="100%" result="displacementMap2"/>
        </filter>
    </defs>
</svg>

我提前定义好了这么一个滤镜,具体啥作用一言两句讲不清楚,感兴趣的可以自己去查询资料,这里就不过多的讲解了;

然后在需要使用的地方应用就好了,我这里已经提前应用好了,所以大家可以直接看效果。

css中使用的话,就是这样:

.loong {
  filter: url(#filter);
}

当然单独这样是不会有动画效果的,还是需要结合js来实现的,代码如下:

const feTurbulence = document.getElementById('feTurbulence');
const animate = () => {
    feTurbulence.setAttribute('baseFrequency', `${Math.random()} ${Math.random()}`);
    requestAnimationFrame(animate);
}
animate();

获取到滤镜的元素,然后设置baseFrequency的值,这样滤镜的效果就会一直发生变化,看起来就像是在呼吸一样。

整体应用下来,看起来就像龙活过来了一样,效果还是挺不错的。

3. 总结

这整个效果围绕着两个部分,一个是css渐变的应用,另一个是svg的应用;

css渐变是前端领域的一个很使用的部分,运用的好可以实现非常多的炫酷的效果,例如我这里的门楼和福字的边框效果;

svg是一个矢量图,体量是非常庞大的,但是它的应用是非常广泛的,现在很多图标,还有图标的动画都是使用svg来实现的,这一块的应用还是非常值得学习的。

完成这整个效果,我也是学到了很多,希望大家也能从中学到一些东西。

最后,祝大家龙年快乐,万事如意,身体健康,财源滚滚,龙马精神,龙年大吉!

原文链接:https://juejin.cn/post/7338229082923253775 作者:田八

(0)
上一篇 2024年2月23日 上午10:48
下一篇 2024年2月23日 上午10:58

相关推荐

发表回复

登录后才能评论