有一段时间没有更新自己的博客了去玩gitpage了,后来觉着自己维护一个静态博客还是有着诸多不便。最终还是回到csdn写写一些文字。至于gitpage就留着作为一个展示自己写的静态项目的个人主页吧。
昨天在codepen里看到一个非常酷的导航菜单动画,研究了许多,写出这篇文章来分享一下这个动画的制作流程。
成品展示
别人的项目: https://codepen.io/lbebber/pen/pvwZJp
本人的模仿项目: https://codepen.io/emiyayang/pen/zjWxVd?editors=1100
项目解构
代码是拿笔者的项目来说的,笔者尽量用最精简的代码实现全部的效果。
此外,因为讲解过程是逐步分解的,所以笔者对每个步骤的代码都有选择性做了删减。完整demo请查看codepen链接。
HTML结构
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<nav class='menu'>
<input class='menu-open' type="checkbox" id='menu-open' >
<a class='menu-item' href="#"><i class="fa fa-bar-chart"></i> </a>
<a class='menu-item' href="#"><i class="fa fa-envelope"></i></a>
<a class='menu-item' href="#"><i class="fa fa-heart"></i></a>
<label class='menu-open-btn' for='menu-open'>
<span class='line'></span>
<span class='line'></span>
<span class='line'></span>
</label>
</nav>
导航菜单的html结构十分简单,nav标签里有一个checkbox作为隐藏的触发器,一个label绑定这个触发器用于设置主按钮的外观,3个a作为菜单展开子项应用了bootstrap提供的icon。label下有三个span,作为菜单按钮的三条横线。
其中,最关键的存在就是这个checkbox了。它的选中与非选中的两个状态就实现动画的trigger。
css布局
.menu-open {
display: none;
}
.menu {
position: absolute;
/*预留空间供菜单toggle*/
width: 300px;
height: 50px;
left: 50%;
top: 50%;
/*25px为圆宽度的一半,这样处理使其水平、垂直居中显示*/
transform: translate(-25px, -50%);
}
.menu-item,
.menu-open-btn {
position: absolute;
width: 50px;
height: 50px;
border-radius: 100%;
cursor: pointer;
background-color: white;
}
css3实现简单的放缩动画
.menu-open-btn {
/*用transform定义一般情况也就是unchecked时的状态*/
transform: scale(1, 1);
/*规定该状态下的transition,包括时间函数以及持续时间*/
transition-timing-function: linear;
transition-duration: 200ms;
}
/* 选择器知识: A ~ B, A后的兄弟节点B全部被选中 */
.menu-open:checked ~ .menu-open-btn {
/*定义checked时,主按钮的transform状态。*/
transform: scale(0.8, 0.8);
/*这里也可以再定义transition属性,省略则用一般情况下的transition*/
}
如此,就实现了一个简单的可回滚的动画。按下按钮时按钮就会缩小,再按则会复原。这里也可以看出为什么要将checkbox放到最前面,这样处理就可以使用兄弟选择器进行选择了。
css添加菜单图标动画
.menu-open-btn {
text-align: center;
overflow: hidden;
}
.line {
display: inline-block;
position: absolute;
width: 25px;
height: 4px;
top: 50%;
left: 50%;
/*此处注意下居中的技巧, 这里没有用transform: translate(-50%, -50%)
是由于这样做会使line偏离原点,让其他两条横线的坐标变换变得更为复杂。*/
margin-left: -12.5px;
margin-top: -2px;
background-color: #0c9;
transition-duration: 500ms;
}
/*正常状态下的坐标*/
/*选择器知识: A:nth-child(i), 判断A父元素的第i个子元素是否为A,是则选中*/
.line:nth-child(1) {
transform: translate(0, -8px);
}
.line:nth-child(2) {
}
.line:nth-child(3) {
transform: translate(0, 8px);
}
/*选中状态下的坐标*/
.menu-open:checked ~ .menu-open-btn .line:nth-child(1) {
transform: translate(0, 0) rotate(45deg);
}
.menu-open:checked ~ .menu-open-btn .line:nth-child(2) {
opacity: 0;
}
.menu-open:checked ~ .menu-open-btn .line:nth-child(3) {
transform: translate(0, 0) rotate(-45deg);
}
这段css实现了三条横线变为×的这样一个动画。trigger也是checkbox的点击事件。这里三条横线的布局也有点意思,一开始大家都是绝对定位于同一坐标——叠在一起的。然后利用translate分离上下两条横线,中间那条不变。
css实现展开多个菜单子项
主按钮的动画做完了,接下来该展开3个菜单子项了。
.menu-item {
transition-duration: 300ms;
text-align: center;
}
.menu-item > i {
/*注意这里令图标在圆里垂直居中的方法: 让子元素的行高与父元素等高*/
line-height: 50px;
color: #00cc99;
}
.menu-open:checked ~ .menu-item:nth-child(2) {
transform: translate(70px, 0);
}
.menu-open:checked ~ .menu-item:nth-child(3) {
transform: translate(140px, 0);
}
.menu-open:checked ~ .menu-item:nth-child(4) {
transform: translate(210px, 0);
}
svg + css3实现液态动画
神奇的地方来了,利用css3的Filter配合svg可以让导航菜单的展开与收缩带上“液化”的效果。
<!-- filters, 这段svg对笔者来说还是串神秘代码呢T_T -->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="shadowed-goo">
<feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="10" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" />
<feGaussianBlur in="goo" stdDeviation="3" result="shadow" />
<feColorMatrix in="shadow" mode="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 -0.2" result="shadow" />
<feOffset in="shadow" dx="1" dy="1" result="shadow" />
<feComposite in2="shadow" in="goo" result="goo" />
<feComposite in2="goo" in="SourceGraphic" result="mix" />
</filter>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="10" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" />
<feComposite in2="goo" in="SourceGraphic" result="mix" />
</filter>
</defs>
</svg>
这块画个饼,以后再看…… css上的配置就简单了。
.menu {
filter: url("#shadowed-goo");
}
出入兼容性考虑,css属性transform、filter都要带上前缀-webkit-复写一遍。
技术小结
- 选择器语法:
- A + B
判断紧跟A的下一个兄弟节点是否为B,若是则选中;
- A ~ B
选中A后的全部兄弟节点B
- A: nth-child(i)
若A的父元素的第i个子节点为A,则选中 - 布局方法
- 完全居中(一)
绝对定位,定长定宽,上5左5,transform: translate(-50%, -50%);
- 完全居中(二)
子节点为行内元素,其行高设与父元素等高,父元素设text-align: center - 一个css3动画的基本实现流程
抛开@frame不谈,首先要确定一个能用css体现的trigger。像该项目就是checkbox的checked伪类,接着据此就可以设置两个状态。在一般状态下设置transition-duration,只要两个状态的特定动画属性或者transform存在差异就能生成动画。