Jqueryでフリック対応カルーセル系スライダーのメニュー

提供:wiki - PCスキルの小技・忘却防止メモ
移動: 案内, 検索

jqueryでフリック対応カルーセル系スライダー。スマホとかである指でスライドさせるってヤツをメニューで使いたい。

基本

jqueryでフリック対応カルーセル系スライダー」だとスマホでページ遷移してくれない。

上記の流れは

  1. スライダー部の親要素をタップでイベント発生→スライド開始
  2. タップを解除(?・・・指を離した)でスライド終了

今回のメニューにする場合の流れは

  1. スライダー部の親要素をタップでイベント発生→スライド開始&時間取得
  2. タップを解除(?・・・指を離した)したときに経過時間が短かったらリンク、短くなかったらスライド終了

ソース

説明が面倒なのでソース内にコメントをいれておく。

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>フリック対応カルーセル系スライダーメニュー</title>
<style type="text/css">
ul, li {
  margin: 0;
  padding: 0;
  list-style: none;
}
.viewArea{
  width: 240px;
  height: 150px;
  margin: 0 auto;
  position: relative;
  overflow: hidden;
}
.slider{
  position: absolute;
  top: 0;
  left: 0;
  display: table;
  width: auto;
}
.slider li{
  width: 120px;
  display: table-cell;
}
</style>
<script type="text/javascript" src="jquery.min.js"></script>
</head>

<body>
  <div class="viewArea">
    <ul class="slider">
      <li><a href="#"><img src="image01.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image02.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image03.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image04.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image05.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image06.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image07.jpg" alt="image"></a></li>
      <li><a href="#"><img src="image08.jpg" alt="image"></a></li>
    </ul>
  </div>
  
<script type="text/javascript">
$(function(){
  var isTouch = ('ontouchstart' in window);
  var touched = false;
  
  //スライダー調整
  var tgElm = $(".slider");
  var elmWidth = tgElm.find("li").outerWidth(true);
  var sldWidth = ( elmWidth * tgElm.find("li").size() ) + "px";
  tgElm.css({"width":sldWidth});
  
  // いい方法が分からないので時間で対応させる。
  var touchTimer = false;
  
  // 「.slider」じゃなくて「.slider a」でリンクパスを保存しておく
  var lnk = "";
  $(".slider a").on({
     'touchstart mousedown': function(e) {
        lnk = $(this).attr("href");
     }
  });

  /**
   * ※「.slider」の外でタッチが終了したときの処理が必要。
   **/
   $(".slider").bind({
    /* タッチの開始、マウスボタンを押したとき */
    'touchstart mousedown': function(e) {
      // ページが動いたり、反応を止める
      e.preventDefault();
      
      // 開始時間の取得 
      touchTimer = +new Date();
      
      // マウスの(開始)位置(touchmove イベントを通らず終了したときのために必ず覚えておくこと)
      this.touchX = (isTouch ? event.changedTouches[0].pageX : e.pageX);
      
      // 現在の 要素 の場所を覚えておく
      this.elmentX = $(this).position().left;
       
      // タッチ処理を開始したフラグをたてる
      touched = true;
    },
    /* タッチしながら移動、マウスのドラッグ */
    'touchmove mousemove': function(e) {
      if (!touched) {
        // 開始していない場合は動かないようにする・過剰動作の防止
        return;
      }
       
      // ページが動くのを止める
      e.preventDefault();
       
      // 移動前と移動先の座標から移動値を求め、要素の移動先の座標を取得。
      var tmpX = isTouch ? event.changedTouches[0].pageX : e.pageX;
      this.elmentX = this.elmentX - (this.touchX - tmpX );
      
      // 要素を移動させる
      $(this).css({left:this.elmentX});
      
      // 移動先(pageX)を覚えておく
      this.touchX = (isTouch ? event.changedTouches[0].pageX : e.pageX);
    },
    /* タッチの終了、マウスのドラッグの終了 */
    'touchend mouseup': function(e) {
      if (!touched) {
        // 開始していない場合は動かないようにする・過剰動作の防止
        return;
      }
      
      // 経過時間が100ミリ秒未満ならリンクさせる
      if ( (+new Date() - touchTimer) < 100 ) {
             // 動いているか分からないのでレイヤーをかぶせておく(画面を真っ黒にしている。半透明にしたほうがいいと思う)
             var layerDiv = $('<div style="background-color:#000"></div>').css({
                         'width'  : $(window).width(),
                         'height' : $(document).height(),
             });
             $("body").prepend(layerDiv);
             
             window.location = lnk;
             return false;
      }
      
      var tmpX = Math.ceil(this.elmentX / elmWidth) * elmWidth;
      var tmpHalf = tmpX - Math.ceil(elmWidth / 2);
      
      if ( -(Math.ceil(elmWidth / 2)) < this.elmentX) {
        this.elmentX = 0;
      }
      else if (this.elmentX < -(tgElm.width()-tgElm.parents(".viewArea").width())) {
        this.elmentX = -(tgElm.width()-tgElm.parents(".viewArea").width());
      }
      else if (tmpHalf <= this.elmentX) {
        this.elmentX = tmpX;
      }
      else {
        this.elmentX = tmpX - elmWidth;
      }
      $(this).animate({left:this.elmentX+"px"}, 500);
      
      // タッチ処理は終了したため、フラグをたたむ
      touched = false;
      touchTimer = false
    },
    'click': function(e) {
      if ( (+new Date() - touchTimer) < 100 ) {
             // 動いているか分からないのでレイヤーをかぶせておく(画面を真っ黒にしている。半透明にしたほうがいいと思う)
             var layerDiv = $('<div style="background-color:#000"></div>').css({
                         'width'  : $(window).width(),
                         'height' : $(document).height(),
             });
             $("body").prepend(layerDiv);
             
             window.location = lnk;
      }
      return false;
    }
  });
  
  $(document).bind("touchend mouseup", function(e){
    if (touched && e.target.getAttribute("class") != "slider") {
      if (isTouch) {
        $(".slider").trigger("touchend");
      }
      else {
        $(".slider").trigger("mouseup");
      }
    }
  });
});
</script>
</body>
</html>

※「.viewArea(240x150)」の表示エリア内に「.slider li(幅120px)」の画像を並べるって感じ。

関連項目