アニメーションするハンバーガーボタンつくる

今日日(きょうび、って漢字にするとめちゃ読みずらい)、ハンバーガーボタンは動いて当然だよね、って気風だし、毎回つくるの面倒なのでさくっとコピペできるボタン用意した。

See the Pen animated hamburger button by ruemui (@ruemui) on CodePen.

ポイント

  • モジュールとして使いやすいようボタンだけ
  • なるべく少ないコード
  • カスタマイズしやすいよう色々まとめた

使い方

HTML

<div class="header__btn"><span>MENU</span></div>

CSS

.header__btn {
  $width: 50px;
  $strokeWidth: 4px;
  $distance: 15px;
  $duration: .4s;
  $color: #000;

  position: absolute;
  width: 50px;
  height: 50px;
  font-size: 0;  
  &:hover {cursor: pointer;}
  span {
    border-bottom: $strokeWidth solid $color;
    width: $width;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -($width / 2);
    transition: all $duration ease;
    &:before, &:after {
      content: '';
      display: inline-block;
      border-top: $strokeWidth solid $color;
      width: $width;
      position: absolute;
      left: 50%;
      margin-left: -($width / 2);
      transition: all $duration ease;
    }
    &:before {
      top: -($distance);
    }
    &:after {
      top: $distance;
    }
  }
}
// when the menu opens
.header__btn--opened { 
  span {
    border-bottom: none;
    &:before {
      transform: rotate(-45deg);
      top: 0;
    }
    &:after {
      transform: rotate(45deg);
      top: 0;
    }
  }
}

/* to display button at the center */
.header__btn {
  margin: auto;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
  • $width: ボタンの幅
  • $strokeWidth: ハンバーガーの線幅
  • $distance: 線と線の間隔
  • $duration: アニメーションする時間
  • $color: メニューの色

spanが中央線、:beforeが上の線、:afterが下の線になっていて、上下の線の回転角を変えて色々できる。たとえば、:before、を45度:afterを135度にしたりしてもいい。

js

var $btn = $('.header__btn');
$btn.on('click', function(e) {
  e.preventDefault();
  $(this).toggleClass('header__btn--opened');
});

e.preventDefault(); いらないけどトリガーのdivaに変えてもいいように一応入れておいた。

あと、cssだけでやるのも(ちょっと前だけど)流行っていたのでつくっといたよ。

See the Pen CSS only animated hamburger button by ruemui (@ruemui) on CodePen.

HTML

<div class="header__btn">
  <input type="checkbox" id="menu">
  <label for="menu">MENU</label>
</div>

CSS

$width: 50px;
$strokeWidth: 6px;
$distance: 15px;
$duration: .4s;
$color: #000;
 
label:hover {cursor: pointer;}
input {
  display: none;
}
label {
  font-size: 0;
  width: $width;
  height: $width;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-left: -($width / 2);
  transition: all $duration ease;
  &:before, &:after {
    content: '';
    display: inline-block;
    border-top: $strokeWidth solid $color;
    width: $width;
    position: absolute;
    left: 50%;
    margin-left: -($width / 2);
    transition: box-shadow ($duration / 4),
                transform $duration ($duration / 2),
                top $duration;
  }
  &:before {
    top: -($distance);
    box-shadow: 0 $distance 0 $color;
  }
  &:after {
    top: $distance;
  }
}

// when the menu opens
input:checked { 
  & ~ label {
    &:before {
      transform: rotate(-45deg);
      top: 0;
      box-shadow: ($distance * 2) $distance 0 lighten($color, 100%);
    }
    &:after {
      transform: rotate(45deg);
      top: 0;
    }
  }
}

この人 CSS3 Only Menu

のやり方を参考に。

labelをトリガーにすると本当にボーダーの上をクリックしなければいけなかったのでlabelに高さを持たせて、 三本の線はlabel:beforebox-shadowで。box-shadowでつくる方が色々線のコントロールはしやすかった。

比較して

js使わないほうが条件こぼれとかなさそうだし、反応もすこしだけいい気がする。ただメニューで使うときにはinput:checkedを使ってメニューオープン時のスタイルを定義しなきゃいけないので、CSSの見通し悪くなりそう。