LoginSignup
9
15

More than 5 years have passed since last update.

全画面モーダル(CSS Animation)

Posted at

概要

  • スマートフォン向け。
  • ボタンを押すと画面いっぱいにモーダルが表示される。
  • モーダル右上の「×」ボタンで閉じる。
  • 表示と非表示をCSS Animationで演出。

CSS Animation

CSS Animation - Can I use

  • スマートフォンだと、-webkit-のプレフィックス使えば全てカバーできる状況と言っていい(Android 4以上)。
  • IE9が対象外で良いなら、PCでも使える。

サンプルコード

<div class='jscContents'>
  <article>
    <h1 class="title">タイトル</h1>
    <p>テキストテキスト</p>

    <div class="jscModalTrigger btn">モーダルを開く</div>
  </article>

  <article>
    <h1 class="title">タイトル</h1>
    <p>テキストテキスト</p>

    <div class="jscModalTrigger btn">モーダルを開く</div>
  </article>
</div>

<div id="modal" class="jscModal modal">
  <header class="modalHeader">
    <h1 class="modalTitle">全画面モーダル</h1>
    <div class="jscBtnClose btnClose"></div>
  </header>

  <ul class="modalList">
    <li>Chrome</li>
    <li>Firefox</li>
    <li>IE</li>
    <li>Opera</li>
    <li>Safari</li>
  </ul>

  <ul class="modalList">
    <li>Chrome</li>
    <li>Firefox</li>
    <li>IE</li>
    <li>Opera</li>
    <li>Safari</li>
  </ul>
</div>
html,
body {
  height: 100%;
  min-height: 100%;
}

body {
  font-family: 'meiryo';
}

.dn {
  display: none !important;
}

article {
  box-sizing: border-box;
  padding: 10px;
  width: 100%;
  padding: 10px;
  height: 400px;
  background: #FFFFFF;
}

.title {
  border-bottom: solid 1px #CCC;
}

.title,
.modalTitle,
.modalList {
  margin: 0;
}

.btn {
  cursor: pointer;
  width: 100%;
  border-radius: 3px;
  background: #DDD;
  padding: 5px 0;
  text-align: center;
}

.modal {
  display: none;
  opacity: 0;
  position: fixed;
  width: 100%;
  min-height: 100%;
  top: 0;
  left: 0;
  background-color: #F5F5F5;
}

.modal.isOpen {
  display: block;
  opacity: 1;
  animation: scaleUp 0.3s linear;
  -webkit-animation: scaleUp 0.3s linear;
}

.modal.isStatic {
  position: static;
}

.modal.isClose {
  display: block;
  animation: scaleDown 0.3s linear;
  -webkit-animation: scaleDown 0.3s linear;
}

@keyframes scaleUp {
  0% {
    opacity: 0;
    transform: scale(0, 0);
  }
  100% {
    opacity: 1;
    transform: scale(1, 1);
  }
}

@-webkit-keyframes scaleUp {
  0% {
    opacity: 0;
    transform: scale(0, 0);
  }
  100% {
    opacity: 1;
    transform: scale(1, 1);
  }
}

@keyframes scaleDown {
  0% {
    opacity: 1;
    transform: scale(1, 1);
  }
  100% {
    opacity: 0;
    transform: scale(0, 0);
  }
}

@-webkit-keyframes scaleDown {
  0% {
    opacity: 1;
    transform: scale(1, 1);
  }
  100% {
    opacity: 0;
    transform: scale(0, 0);
  }
}

.modalHeader {
  position: relative;
}

.modalTitle {
  font-size: 15px;
  padding: 5px;
  background-color: #3F51B5;
  color: #FFFFFF;
}

.btnClose {
  position: absolute;
  cursor: pointer;
  top: 0;
  right: 0;
  text-align: center;
  width: 33px;
  height: 100%;
  color: #FFF;
}

.btnClose:before,
.btnClose:after {
  position: absolute;
  display: block;
  margin-top: -6px;
  top: 50%;
  content: "";
  width: 10px;
  height: 10px;
  border-color: #FFF;
  border-style: solid;
}

.btnClose:before {
  left: 4px;
  border-width: 2px 2px 0 0;
  transform: rotate(45deg);
}

.btnClose:after {
  right: 3px;
  border-width: 2px 0 0 2px;
  transform: rotate(-45deg);
}

.modalList {
  padding: 10px;
  list-style: none;
}

.modalList > li {
  padding: 5px 10px;
  border-radius: 3px;
  border: solid 1px #ddd;
  background-color: #FFF;
}

.modalList > li + li {
  margin-top: 10px;
}
var SAMPLE = SAMPLE || {};

SAMPLE.FullScreenModal = function() {
  this.$trigger = $('.jscModalTrigger');
  this.$modal = $('.jscModal');
  this.$contents = $('.jscContents');
  this.init();
};

SAMPLE.FullScreenModal.prototype = {
  HIDE_CLASS: 'dn',
  OPEN_CLASS: 'isOpen',
  STATIC_CLASS: 'isStatic',
  CLOSE_CLASS: 'isClose',
  init: function() {
    this.$window = $(window);
    this.$btnClose = this.$modal.find('.jscBtnClose');
    this.toggleClassName = [this.OPEN_CLASS, this.CLOSE_CLASS, this.STATIC_CLASS].join(' ');

    this.bindEvent();
  },
  bindEvent: function() {
    var _self = this;

    this.$trigger.on('click', function(e) {
      e.preventDefault();
      _self.openModal();
    });

    this.$modal.on('animationend webkitAnimationEnd', function() {
      _self.toggleModalStatus();
    });

    this.$btnClose.on('click', function(e) {
      e.preventDefault();
      _self.closeModal();
    });
  },
  openModal: function() {
    this.windowScrollTop = this.$window.scrollTop();
    this.$modal.addClass(this.OPEN_CLASS);
  },
  closeModal: function() {
    this.$contents.removeClass(this.HIDE_CLASS);
    this.$modal.toggleClass(this.toggleClassName);
    this.$window.scrollTop(this.windowScrollTop);
  },
  toggleModalStatus: function() {
    var isOpen = this.$modal.hasClass(this.OPEN_CLASS);

    if (isOpen) {
      this.$contents.addClass(this.HIDE_CLASS);
      this.$modal.addClass(this.STATIC_CLASS);
      this.$window.scrollTop(0);
      return;
    }

    this.$modal.removeClass(this.CLOSE_CLASS);
  },
};
new SAMPLE.FullScreenModal();

CSS

基本設定

  • ベースとなる.modalにアニメーション用の.isOpen.isCloseを設定。
  • モーダルは初期状態では非表示なので、ベースにdisplay: none;を設定。
  • アニメーション用クラスにはdisplay: block;を設定した上で、アニメーション用のプロパティを割り当てる。
  • 開いた時には、ベースの非表示設定を上書きする必要があるため、opacity: 1 を設定している。position については、開いた後 staticにしたかったので、設定。
.modal {
  display: none;
  opacity: 0;
  position: fixed;
  width: 100%;
  min-height: 100%;
  top: 0;
  left: 0;
  background-color: #F5F5F5;
}

.modal.isOpen {
  display: block;
  opacity: 1;
  animation: scaleUp 0.3s linear;
  -webkit-animation: scaleUp 0.3s linear;
}

.modal.isStatic {
  position: static;
}

.modal.isClose {
  display: block;
  animation: scaleDown 0.3s linear;
  -webkit-animation: scaleDown 0.3s linear;
}

keyframe

  • アニメーションで変化させたい部分だけ書く。
  • 開く時 → scaleUp、閉じる時→scaleDown として作成。
  • それぞれ、opacityとscaleの変化を設定。
@keyframes scaleUp {
  0% {
    opacity: 0;
    transform: scale(0, 0);
  }
  100% {
    opacity: 1;
    transform: scale(1, 1);
  }
}

@-webkit-keyframes scaleUp {
  0% {
    opacity: 0;
    transform: scale(0, 0);
  }
  100% {
    opacity: 1;
    transform: scale(1, 1);
  }
}

@keyframes scaleDown {
  0% {
    opacity: 1;
    transform: scale(1, 1);
  }
  100% {
    opacity: 0;
    transform: scale(0, 0);
  }
}

@-webkit-keyframes scaleDown {
  0% {
    opacity: 1;
    transform: scale(1, 1);
  }
  100% {
    opacity: 0;
    transform: scale(0, 0);
  }
}

JSでの処理

開く時

  • スクロール位置の保存
  • モーダルに開くアニメーション用のクラスを付ける。

開くアニメーション終了時

  • コンテンツ部分の状態を非表示(display: none)にする。
  • モーダルのpositionをstaticにする。
  • スクロール位置を最上部に持ってくる。

閉じる時

  • コンテンツ部分の非表示用クラスを外す。
  • モーダルの開くアニメーション用クラス、staticクラスを外しつつ、同時に閉じるアニメーション用クラスを付ける。
  • スクロール位置をモーダルを開く前の位置に戻す。

閉じるアニメーション終了時

  • 閉じるアニメーション用クラスを外す(と同時にCSSの設定上非表示になる)

Transitionを利用するパターンとの比較

  • 「アニメーション終了時にstaticにする」という部分をCSSで設定できる。→ できるが、チラツキが気になったのでJSで処理するように修正。
  • display: none;display: block; の変化を伴うコンテンツをアニメーションさせる際、JSでのクラスの付け外し時にsetTimeout()等でタイミングをずらす必要がなくなる。
  • タイミングをずらす必要が無いので、アニメーションさせるコンテンツに対して、display: none;用のクラスだけの付け外しがない。
  • 閉じる時と開く時に別のアニメーションを設定する必要がある。
  • アニメーション終了時に処理を行う場合は、animationend イベントを利用する。
  • JSの処理がクラスの付け外し(+スクロール位置補正)と、特定クラスの有無による分岐処理だけになり、よりシンプルになった。

参考サイト

9
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
15