龙年到了,今天用上所学的技术,画出一条龙,庆祝龙年到来。
废话不多说,直接体验效果先:
;
体验完毕就来讲讲一些实现的细节。
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-size
,background-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);
}
最后就是灯笼了,灯笼是使用一个css
的3d
效果来实现的,首先是一个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
都填色之后,会有一个消息提示,让点击龙的眼睛(其实是龙头的那一块)
,然后龙就会活过来:
可以看看最终的效果,龙的翎毛和龙爪部分会有一个类似呼吸的效果,这一块是通过svg
的filter
来实现的,具体代码如下:
<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 作者:田八