银烛秋光冷画屏,轻罗小扇扑流萤。仲夏之夜,花草从间,萤光舞动,构成一幅诗意的画面。如今用HTML5大显身手,可以做很多有意思的应用和游戏。今天我们要分享一个用纯CSS3做一个萤火虫动画的特效。
先来看看想要的效果,怎样,酷炫吧!然而,这只是张摄影师拍的静态照片,我们用代码实现它,并让萤火虫闪动起来,一起跟我来动手试试吧!
图一 星光下的萤火虫
首先,搭建好基本的HTML结构。从生物学角度看,萤火虫firefly可分为头部head(触须tentacle、眼睛eyes)、胸部pereion、腹部belly,还有两只翅膀wings,因此语义化标签,对其组成拆解,以类划分。
其中,增加了label元素,包围了所有div, for属性绑定checkbox控件input的id,当在label 元素内点击文本时,就会触发控件,浏览器会自动将焦点转到和标签相关的表单控件上,由此checked的true或false,产生点击开启和关闭萤火虫荧光的效果。
好了,基本结构搭建好了,接下来就是强大的CSS3上场了。
本文采用flex弹性盒布局做响应式处理,以方便实现排列、对齐和分配空白空间,布局方式比较灵活。以vh、rem为相对计量单位,这样萤火虫就会根据显示窗口viewport的宽高相应变化。垂直居中排列,背景蓝色由深到浅做线性变换,映照在蓝天下,突出荧光效果。在最外层设置一个wrap包裹整只萤火虫,相对定位。
这里,要用到重要的伪元素 :before和 :after,帮助实现优美的颜色过渡、阴影、叠加和3D效果,而不添加赘余内容扰乱文档本身。以萤火虫的两只眼睛eyes为例,对eyes绝对定位设置好上下左右的宽高大小后,用.eyes:before { top: 0; }为左眼,.eyes:after { bottom: 0; }表示右眼,设置好黑色背景和圆角就生成了双眼的效果了,没有多余的内容。
然后,是头部,关键点是圆角border-radius四个值的设置,让头部显得圆润一些,和阴影box-shadow的层次感;胸部背景色为偏红色,与头腹黑色对比明显;一对触须和眼睛一样要用到伪元素,设置z-index属性为负值,border-radius和border-width的四个值控制触须形状变化。
最关键的部分是,发出荧光的腹部,样式要稍微复杂一些。默认状态下,翅膀关闭,不发出荧光;点击翅膀张开状态下,设置关键帧flicker周期持续4000ms先快后慢的无限止做颜色变换,关键点截取开始、结束深黄色 #fefa01,四分之一、四分之三浅黄色#fffd99,中间为深黄色透明度为0.8,以达到荧光色rgba(254, 250, 1,0.8)的效果,期间阴影也做相应变化。
图二 翅膀关闭的萤火虫
最后,完善翅膀的动态效果,点击后,左右翅膀分别顺逆时针旋转28度。为了让翅膀更好看,可以加上条纹,对背景色做线性变换处理, background: repeating-linear-gradient(#27231e 0%, #27231e 40%, #191613 40%, #191613 60%)。腹部的处理也类似,其他部位再做些微调就OK了。
就这样,纯CSS会闪动发光神奇的萤火虫就完成了!动态效果如下图。
图三 发出荧光的萤火虫
心动不如行动,赶紧去试试吧!
其中,CSS3样式代码附如下:
body {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100vh;
background: radial-gradient(#0a2a43 , #039);
}
.wrap {
flex: none;
position: relative;
width: 10%;
min-width: 8rem;
z-index: 1;
}
.wrap:after {
content: ”;
display: block;
padding-top: 50%;
}
.content {
display: none;
}
.content:checked ~ .firefly .belly {
background: #27231e;
box-shadow: inset 0 0 1.5rem rgba(150, 0, 0, 0.75);
animation: flicker 4000ms ease infinite;
}
.content:checked ~ .firefly .wing-l {
transform: rotate(28deg);
}
.content:checked ~ .firefly .wing-r {
transform: rotate(-28deg);
}
.head {
position: absolute;
top: 15%;
left: 60%;
bottom: 15%;
background: #27231e;
width: 70%;
border-radius: 40% 80% 80% 40%;
box-shadow: inset -0.1rem 0 0 0.3rem rgba(14, 10, 10, 0.2);
}
.eyes {
position: absolute;
top: -5%;
right: 10%;
bottom: -5%;
width: 35%;
}
.eyes:before, .eyes:after {
content: ”;
display: block;
position: absolute;
right: 0;
width: 100%;
box-sizing: border-box;
border-radius: 100%;
background: #0e0a0a;
padding-top: 100%;
}
.eyes:before {
top: 0;
}
.eyes:after {
bottom: 0;
}
.tentacle {
position: absolute;
top: 20%;
left: -150%;
bottom: 20%;
width: 400%;
z-index: -1;
}
.tentacle:before, .tentacle:after {
content: ”;
display: block;
position: absolute;
right: 0;
width: 100%;
box-sizing: border-box;
border:solid #0e0a0a ;
padding-top: 65%;
border-width: 0.2rem .25rem .1rem 0;
}
.tentacle:before {
top: 80%;
border-radius: 0 100% 100% 100%;
}
.tentacle:after {
bottom: 80%;
border-radius: 100% 100% 100% 0;
}
.pereion {
position: absolute;
top: 0;
left: 100%;
bottom: 0;
background: #FF5511;
width: 30%;
border-radius: 20% 80% 80% 20%;
box-shadow: inset -0.1rem 0 0.1rem 0.3rem rgba(0, 0, 0, 0.2);
}
.belly {
position: absolute;
top: 10%;
right: 20%;
bottom: 10%;
background: #27231e;
width: 100%;
transition: all 1000ms ease;
border-radius: 100% 30% 30% 100%;
box-sizing: border-box;
}
.belly:after {
content: ”;
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: repeating-linear-gradient(90deg, transparent 0, transparent 15%, rgba(0, 0, 20, 0.1) 15%, rgba(0, 0, 20, 0.1) 20%);
mix-blend-mode: multiply;
border-radius: 100% 30% 30% 100%;
z-index: 0;
box-shadow: inset -1rem 0 0.2rem 0.3rem rgba(0, 0, 0, 0.1);
}
.wings {
position: absolute;
top: -20%;
right: 0;
bottom: -20%;
left: -5%;
z-index: 1;
}
.wings .wing {
position: absolute;
right: 0;
background: repeating-linear-gradient(#27231e 0%, #27231e 40%, #191613 40%, #191613 60%);
width: 100%;
height: 50%;
transition: all 200ms ease-out;
border: 1px solid #00AA88;
box-sizing: border-box;
box-shadow: inset 0.2rem 0 0.1rem 0.5rem rgba(0, 0, 0, 0.5);
}
.wings .wing.wing-l {
transform-origin: bottom right;
top: 0;
border-radius: 90% 30% 0 20%;
}
.wings .wing.wing-r {
transform-origin: top right;
top: 50%;
border-radius: 20% 0 30% 90%;
}
@keyframes flicker {
0%, 100% {
background: #fefa01;
box-shadow: 0 0 1rem #fefa01, inset -0.25rem 0 0 0.5rem rgba(14, 10, 10, 0.1);
}
25%, 75% {
background: #fffd99;
box-shadow: -1rem 0 8rem 1rem #fefa01, inset -0.25rem 0 0 0.5rem rgba(14, 10, 10, 0.1);
}
50% {
box-shadow: -1rem 0 8rem 1rem rgba(254, 250, 2, 0.8), inset -0.25rem 0 0 0.5rem rgba(14, 10, 10, 0.1);
}
}