css做一个可以变成关闭图标的菜单按钮

不记得什么是时候在某个网站上看到一个菜单按钮, 初始状态是三条横线, 点击打开菜单后可以变成关闭按钮, 这个过程有一个过度动画, 效果像下面这样(因为想不起来是哪个网站了, 所以下面的的gif是我根据印象实现后的效果)

这个东西实现起来挺简单, 但是这个过度动画挺有意思的;

首先是html, 外层一个div.menu元素, 里面放三个span用作三条杠

<div class="menu">
    <span></span>
    <span></span>
    <span></span>
</div>

然后写点css, 设置下menu的宽高和三条横杠, 同时也设置下过度相关的属性; 我这里用了less作为css的预处理器:

@menu-size: 50px;
@menu-stroke: 4px;
@menu-stroke-color: #fff;
@menu-animation-duration: .3s;

.menu {
  display: inline-flex;
  flex-direction: column;
  justify-content: space-around;
  height: @menu-size;
  span {
    width: @menu-size;
    height: @menu-stroke;
    display: block;
    transition-property: opacity transform top;
    transition-duration: @menu-animation-duration;
    background-color: @menu-stroke-color;
  }
  span:nth-child(2){
    transition-duration: .1s;
  }
}

基本的样式写完后写开始写过度之后的样式, 给这个样式取一个名字叫menu-closeable

.menu-closeable {
  position: relative;
  span:nth-child(1) {
    position: absolute;
    transform: rotateZ(45deg);
    top: @menu-size/2;
  }
  span:nth-child(2) {
    opacity: 0;
  }
  span:nth-child(3) {
    position: absolute;
    transform: rotateZ(-45deg);
    
    top: @menu-size/2;
  }
}

这些样式写完后需要通过js来触发, 点击后在让菜单关闭状态和普通状态间进行切换

const el = document.querySelector('.menu');
let clicked = false;
el.addEventListener('click', () => {
  clicked = !clicked;
  if (clicked) {
  el.classList.add('menu-closeable');
  } else {
    el.classList.remove('menu-closeable');
  }
})

这样就完成了, 每次点击菜单, 菜单都会在三条横杠和一个叉叉的状态的间切换, 并且还有过度动画, 就像开头的git里面那样.

完整的演示可以参见Codepen.

Angular中的实现这个动画

模板html

<div class="menu"
     [@menuCloseIcon]="{ value: isCloseIcon ? 'close' : 'menu', params: { size: size + 'px' } }"
     [ngStyle]="{ height: size + 'px' }">
  <span [ngStyle]="spanStyle"
        [@span1Trigger]="{ value: isCloseIcon ? 'close': 'menu', params: { size: size + 'px' } }"></span>
  <span [ngStyle]="spanStyle"
        [@span2Trigger]="{ value: isCloseIcon ? 'close': 'menu', params: { size: size + 'px' } }"></span>
  <span [ngStyle]="spanStyle"
        [@span3Trigger]="{ value: isCloseIcon ? 'close': 'menu', params: { size: size + 'px' } }"></span>
</div>

ts代码

import {
  animate,
  animateChild,
  group,
  query,
  state,
  style,
  transition,
  trigger,
} from '@angular/animations';
import { Component, Input, OnInit } from '@angular/core';

const childrenOption = {
  params: { size: '' },
};

@Component({
  selector: 'cnb-moving-menu',
  templateUrl: './moving-menu.component.html',
  styleUrls: ['./moving-menu.component.less'],
  animations: [
    trigger('menuCloseIcon', [
      transition('* <=> *', [
        group([
          query('@span1Trigger', animateChild()),
          query('@span2Trigger', animateChild()),
          query('@span3Trigger', animateChild()),
        ]),
      ]),
    ]),
    trigger('span1Trigger', [
      state(
        'close',
        style({
          position: 'absolute',
          transform: 'rotateZ(45deg)',
          top: '{{size}}/2',
        }),
        childrenOption
      ),
      state('menu', style({ transform: 'rotateZ(0)' })),
      transition('* => *', [animate('.2s ease-in')]),
    ]),
    trigger('span2Trigger', [
      state(
        'close',
        style({
          opacity: 0,
        }),
        childrenOption
      ),
      state('menu', style({ opacity: 1 })),
      transition('* => *', [animate('.2s ease-in')]),
    ]),
    trigger('span3Trigger', [
      state(
        'close',
        style({
          position: 'absolute',
          transform: 'rotateZ(-45deg)',
          top: '{{size}}/2',
        }),
        childrenOption
      ),
      state('menu', style({ transform: 'rotateZ(0)' })),
      transition('menu <=> close', [animate('.2s ease-in')]),
    ]),
  ],
})
export class MovingMenuComponent implements OnInit {
  @Input() size = 24;
  @Input() stroke = 3;
  @Input() transitionDuration = '.3s';
  @Input() isCloseIcon = false;
  @Input() strokeColor = '#fff';
  spanStyle = {
    height: this.stroke + 'px',
    width: this.size + 'px',
    'background-color': this.strokeColor,
  };

  constructor() {}

  ngOnInit(): void {}
}

样式文件

:host {
  display: inline-flex;
  align-items: center;
}

.menu {
  display: inline-flex;
  flex-direction: column;
  justify-content: space-around;

  span {
    width: @menu-size;
    height: @menu-stroke;
    display: block;
  }
}
posted @ 2021-05-26 23:38  Laggage  阅读(522)  评论(0编辑  收藏  举报