Modal

モーダル表示用の汎用関数

詳しい導入事例は導入方法を参照してください。

他のライブラリでも使用する汎用型のモーダル表示関数

  • 横サイズ
  • アクションボタン
  • 縦長テスト

使い方

下記関数で実行する。

modalOpen('表示要素','スワイプ設定','body用クラス','アクションボタンクラス','サイズ指定');

例)modalOpen(hoge,'swip','demo',['download','window'],'500');

  • 第一引数:モーダルの中で表示する要素の引数を指定。
  • 第二引数:スワイプイベントに使用するクラス名を指定する。+-null-+ +-swip-+
  • 第三引数:なんのモーダルを表示しているか判別するためのクラスをbodyに追加。
  • 第四引数:追加するアクションボタン用のクラス名を指定。追加したボタンのイベントは別途必要。
  • 第五引数:モーダルのサイズ指定。
    画面幅:+-fill-+
    内容幅:+-hug-+
    固定幅:直接サイズを記述。数字のみ。

アクションボタンは*-i class="ico-mui"-*のHTMLが追加されるので、指定したクラス名を使ってアイコンのCSSも別途必要です。

  • CSS
  • Script
#mod_modal {
  display: none;
  position: relative;
  z-index: 1001;
}
.mod_modal-sec ,
.mod_modal-bg {
  position: fixed;
  top: 0;
  left: 0;
}
.mod_modal-sec {
  max-width: 90%;
  max-height: 90%;
  min-width: 400px;
  margin: auto;
  padding: 24px;
  border-radius: var(--radius-xm);
  background: var(--bg-paper);
  box-shadow: var(--shadow-main);
  box-sizing: border-box;
  right: 0;
  bottom: 0;
  z-index: 1001;
}
.fill .mod_modal-sec {
  width: 90%;
  height: 90%;
}
.hug .mod_modal-sec {
  width: fit-content;
  height: fit-content;
}
.mod_modal-scroller {
  height: 100%;
  margin-top: -40px;
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
  position: relative;
  z-index: 1003;
}
.mod_modal-body {
  width: 100%;
  height: 100%;
  margin-top: -40px;
}
.mod_modal-action {
  width: fit-content;
  margin-left: auto;
  border-radius: var(--radius-round);
  display: flex;
  justify-content: flex-end;
  position: sticky;
  top: 0;
  right: 0;
  z-index: 1004;
}
.mod_modal-action-btn {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-circle);
  color: inherit;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: .2s;
}
.mod_modal-action-btn:hover {background: var(--hover);}
.mod_modal-action-btn.close .ico-mui::before {content: '\e5cd';}
.mod_modal-scrollbar {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 24px;
  left: 0;
  z-index: 1002;
}
.mod_modal-scrollbar-btn {
  width: 4px;
  border-radius: var(--radius-xs);
  background: rgb(0 0 0 / .6);
  position: absolute;
  right: 8px;
}
.scrl.hug .mod_modal-sec {height: 100%;}
.scrl .mod_modal-body {margin-top: 0;}
.mod_modal-bg {
  width: 100%;
  height: 100%;
  background: var(--overlay);
  z-index: 1000;
}

@media screen and (max-width: 768px) {
  #mod_modal {
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    position: fixed;
    top: 0;
    left: 0;
  }
  .mod_modal-sec {
    min-width: 50%;
    max-height: 95%;
    padding: 8px;
    position: relative;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .fill .mod_modal-sec {height: 95%;}
}
var modal_frag = false;
//モーダル設置
function modalOpen(obj,swip,cl,act,size){
  $bd.append('<div id="mod_modal"><section class="mod_modal-sec"><ul class="mod_modal-action"><li class="mod_modal-action-li"><button class="mod_modal-action-btn close"><i class="ico-mui"></i></button></li></ul><div class="mod_modal-body"></div></section><div class="mod_modal-bg"></div></div>');
  $('.mod_modal-body').append(obj);
  let $mod_modal = $('#mod_modal');
  let $mod_modalSec = $('.mod_modal-sec');
  let $mod_modalBody = $('.mod_modal-body');

  //スワイプタイプ追加
  if(swip !== ''){$mod_modalSec.addClass(swip);}

  //bodyにclass追加
  if(cl !== ''){
    $bd.addClass(cl);
    $rt.data('dellClass', cl);
  }

  //アクションボタン追加
  if(act !== ''){
    for(var i = 0; i < act.length; i++) {
      $('.mod_modal-action').prepend('<li class="mod_modal-action-li"><button class="mod_modal-action-btn ' + act[i] + '"><i class="ico-mui"></i></button></li>');
    }
  }

  //サイズ設定
  if(size === 'fill' || size === 'hug'){
    $mod_modal.addClass(size);
  } else if($.isNumeric(size)){
    $mod_modalSec.width(size);
  }

  //実行・表示
  if(modal_frag != true){noscroll();}
  $mod_modal.fadeIn(200).css('display','flex');

  //スクロールバー追加
  let secHeight = $mod_modalSec.height()
  let objHeight = $mod_modalBody.children().height();
  if(objHeight  > secHeight){
    $mod_modal.addClass('scrl');
    if($win.width() > 769){
      let scrollerHeight = parseInt(secHeight * secHeight / objHeight);
      
      $mod_modal.addClass('scrl');
      $mod_modalBody.wrap('<div class="mod_modal-scroller"></div>');
      $mod_modalSec.append('<div class="mod_modal-scrollbar"><div class="mod_modal-scrollbar-btn"></div></div>');
      $('.mod_modal-scrollbar-btn').height(scrollerHeight + 'px');

      let scrollerTrack = secHeight - scrollerHeight;
      $('.mod_modal-scroller').on('scroll', function(){
        let offset = $(this).scrollTop() * scrollerTrack / (objHeight - secHeight);
        $('.mod_modal-scrollbar-btn').css('transform','translateY(' + offset + 'px)');
      });
    }
  };
}

//クローズトリガー
$bd.on('click','.mod_modal-action-btn.close , .mod_modal-bg',function(){modalClose();});

//クローズアクション
function modalClose(){
  $('#mod_modal').fadeOut();
  if(modal_frag != true){noscroll();}
  modal_frag = true;

  setTimeout(function(){
    $('#mod_modal').remove();
    if($rt.data('dellClass') !== ''){
      $bd.removeClass($rt.data('dellClass'));
    }
  modal_frag = false;
  },600);
}

//スワイプ 縦横
var isTouch = ('ontouchstart' in window);
$bd.on({
  'touchstart': function(e){
    if($('#mod_modal.scrl')[0]){return;}
    this.pageX = (isTouch ? event.changedTouches[0].pageX : e.pageX);
    this.pageY = (isTouch ? event.changedTouches[0].pageY : e.pageY);
    this.touched = true;
  },
  'touchmove': function(e){
    if(!this.touched){return;}
    this.pageXm = (isTouch ? event.changedTouches[0].pageX : e.pageX);
    this.pageYm = (isTouch ? event.changedTouches[0].pageY : e.pageY);
    if(this.pageYm != 0){
      var yc = this.pageYm - this.pageY + 1;
      var yco = yc / 160;
      var ycod = Math.sqrt(Math.pow(yco,2));
      var opc = 1 - ycod;
      $('.mod_modal-sec').css({
        'transform':'translateY(' + yc + 'px)',
        'opacity':opc,
        'transition':'none'
      });
      nowTouch = true;
    }
  },
  'touchend': function(e){
    if(!this.touched){return;}
    this.touched = false;
    this.left = this.pageX - (isTouch ? event.changedTouches[0].pageX : e.pageX);
    this.top = this.pageY - (isTouch ? event.changedTouches[0].pageY : e.pageY);

    if(this.top == 0 && this.left == 0){
      modalClose();
      return false;
    } else if(this.top > 60){
      $('.mod_modal-sec').css('transform','translateY(-150%)');
      modalClose();
      nowTouch = false;
    } else if(this.top < -60){
      $('.mod_modal-sec').css('transform','translateY(150%)');
      modalClose();
      nowTouch = false;
    } else {
      $('.mod_modal-sec').css({
        'transform':'translateY(0)',
        'opacity':'1',
        'transition':'0.1s'
      });
    }
  }
}, '.mod_modal-sec.swip');