【手把手教学】使用原生svg + css3去实现一个液态导航菜单

有一段时间没有更新自己的博客了去玩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存在差异就能生成动画。

 
 
 
 

猜你喜欢

转载自blog.csdn.net/qq_29977681/article/details/80684646