ぶるーすくりーん

ぽんこつプログラマ日記

レスポンシブなサイドメニューを表示させる

絶賛クライアントサイドお勉強中です。

ここ数日レスポンシブなメニューの表示の仕方について、がちゃがちゃやってたのをまとめます。

最近のモバイルアプリで、左からメニューがしゅっと出てくるタイプの真似をCSSでやってみました。
もっとこうした方がいいよーとかあったら教えて下さい。

完成形

下みたいな感じで、
デスクトップとかタブレット横向きの場合はMenu常に表示。
モバイルとかウィンドウ小さくしている場合は、ハンバーガーボタンが現れてそれ押すとメニューが現れます。
また、左隅でスワイプしてメニュー表示、関係ないとこクリックしてメニュークローズもできます。

f:id:tajima0111185:20140711224537p:plain

f:id:tajima0111185:20140711224549p:plain

ソースコード

以下にコードの完成形がコミットしてあるので、もしよければ参考にしてください。

mid0111/menu-responsive-sample

ヘッダパネルを常に表示

メニューボタンは常に押せる位置にないといけないので、今回はGmailクライアントのデザインを参考に、固定のヘッダパネルを用意して、隠れハンバーガーを常に表示しておくというデザインにしてみました。

まずは、HTMLで以下のようにheader-panelとしてハンバーガーとタイトルを含む人のdiv要素を定義。
※その下のcontentにはメニューやら本文があとから入ります。

    <div class='header-panel'>
      <div class='.header-container'>
        <img class='hamburger' src='./hamburger.svg' alt='*' />
        <span class='title'>Responsive Menu Sample</span>
      </div>
    </div>

    <div class='content'>
    </div>

次にcssを書いていきます。
下のサンプルでは、色とかフォントみたいな細かい好みの設定は省略しているものもあります。

html, body {
    height: 100%;
}

body {
    margin: 0;
    padding: 40px 0 0 0;  /** ヘッダの分上に40pxの余白をつくる **/
}

/** キモはこのクラス **/
.header-panel {
    position: fixed;
      z-index: 1000;
    top: 0px;
    left: 0;
    width: 100%;
    heigho: 40px;
    background-color: rgb(240, 240, 240);
    font-size: x-large;
}

/** ハンバーガーやタイトルを下寄せに **/
.header-container {
    position: absolute;
    bottom: 0;
}

.hamburger {
    position: fixed;
    left: -15px; /** ハンバーガーを半分にするための設定 **/
}

.title {
    float: left;
    margin-left: 20px;
}

ポイントは.header-panelクラスのpositionfixedにして上に固定してあることと、bodyのパディングを指定して、パネルの位置にかぶらないようにしているとこです。

メニューパネルをつくる

まずは、モバイル版からつくっていきます。

モバイル版のしゅっと出てくるデザインは、メニューパネルの位置を画面左側に追い出された状態で定義して、あとはハンバーガーが押された時に位置を画面内に定義しなおすという方法で実現しています。

HTMLのcontentにメニューパネルとメインを用意します。
いろいろと動作確認するために、メインには画面におさまらないくらいの行数をふくめておくとよいです。(下のサンプルではページの邪魔なので省略)
あと、コンテンツごとにバックグランド色を分けておくとよいです。

    <div class='content'>

      <nav class='menu-panel'>
        <a href='#'>Home</a>
        <a href='#'>List</a>
        <a href='#'>Message</a>
        <a href='#'>Profile</a>
      </nav>

      <div class='main'>
        <h1>はろーわーるど</h1>
        <p>This is responsive menu sample.</p>
        <p>Change browser size.</p>
      </div>
    </div>

次にデザインの定義。
まずは、メニューパネルがオープンな状態を定義していきます。

.content {
    width: 100%;
    min-height: 100%;
}

.menu-panel, .main, .swipe, .fade-layer {
      top: 0;
    float: left;
    min-height: 100%;
}

.menu-panel {
    position: fixed;  /** メニューは動作を独立させたいのでfixed指定 **/
    width: 240px;
      z-index: 500;
      left: 0px;
    background: rgb(245, 245, 245);
    box-shadow: 0px 3px 2px rgba(0, 10, 10, 0.2);
}

.menu-panel a {
    padding: 1em;
    display: block;
      font-size: 1.1em;
      font-weight: 300;
    padding-top: 1em;
    box-shadow: 0px 1px 1px rgba(50, 80, 80, 0.1);
}

.main {
    margin-left: 20px;
}

いい感じに画面定義できたら、メニューパネルを画面から追い出します。

.menu-panel {
    position: fixed;
    width: 240px;
      z-index: 500;
      left: -240px;  /** 画面の左に追い出す **/

メニューパネルのアニメーション定義

メニューパネルをオープンしたり、クローズしたりするには、.menu-panelの左位置を移動させることで実現できます。
この切替えは、ハンバーガーメニューが押されたときに、DOMを操作してメニューパネルのクラスにmenu-openクラスを追加したりはずしたりすることで可能です。

ハンバーガーメニューが押下されたときにDOMを書き換えるjavascriptを追加します。

(function() {

  'use strict';

  var querySelector = document.querySelector.bind(document);
  var menuPanel = querySelector('.menu-panel');
  var hambuger = querySelector('.hamburger');

  function toggleMenu() {
    menuPanel.classList.toggle('menu-open');
  };

  // Menu open
  hambuger.addEventListener('click', toggleMenu);
})();

そして、cssmenu-openの定義を追加します。
左橋の位置が0px になるように移動させます。

.menu-open {
    -webkit-transform: translate(240px, 0);
          transform: translate(240px, 0);
}

これで、ハンバーガーボタンでメニューパネルが現れたり消えたりするようになりました。

さらにこのぱっと現れたり消えたりするのを、左側からしゅっと現れたり消えたりするようにするには、csstransitionプロパティを利用します。
これは、ある特定のcss切り替えをアニメーションちっくにやってくれるすぐれものです。

メニューパネルのcsstransitionを追加。

.menu-panel {
    position: fixed;
    width: 240px;
      z-index: 500;
      left: -240px;
    background: rgb(245, 245, 245);
    box-shadow: 0px 3px 2px rgba(0, 10, 10, 0.2);

    transition: all 0.4s;
}

モバイルとデスクトップの切り替え

さて、なんとなくモバイルはできました。
つぎにデスクトップ版対応していきます。

モバイル・デスクトップの切り替えは、ウィンドウの横幅で切り替えます。
下の例では、850px以上ある場合は、各要素をこの値にしてねって宣言しています。

@media only screen and (min-width: 850px) {
    /** タイトル領域もちょっと大きく **/
    .header-panel {
        height: 60px;
        font-size: xx-large;
    }

    .hamburger {
        visibility: hidden;  /** ボタンは不要なので隠す **/
    }

    .title {
        float: left;
        margin-left: 20px;
    }

    .menu-panel {
        left: 0px;    /**最初っからメニューパネル表示 **/
        top: 60px;  /** タイトル領域もちょっと大きく **/
    }

    .main {
        position: absolute;
        top: auto;
        left: 240px;  /**メニューパネルの分右へ **/
    }

}

これで、あなたもれすぽんしぶです。

おわりに

ちょっとまとめが長くなってしまったので省きますが、コミットしてあるソースの方では、下みたいなこともしています。

  • モバイル版でメニューがオープンのときは、裏側のコンテンツを暗くする

    .fade-layerhiddenで作成しておいて、メニューがアクティブになるタイミングで.fade-layerクラスをvisibleにしています。

  • モバイル版で左隅スワイプしたらメニューオープン、メニュー以外のとこクリックしたらメニュークローズ

    左隅に専用の.swipeって名前のdiv切って、スワイプ動作をリッスンしてメニューオープン。上の.fade-layer領域でリッスンしてメニュークローズしています。

だんだんcssと仲良くなってきた感。