jQueryでフリックを実装する場合に縦スクロールできなくなる問題を解決する方法
以前jQueryでプラグインを使わずにフリックを実装する方法についてご紹介いたしましたが、
こちらの方法を使った場合に、1つ問題が発生します。
それは、スマホやタブレットでは、
フリックできるようにした要素の上ではページのスクロールができなくなってしまうという問題です。
これは、touchmoveイベントの本来の動作であるページのスクロール動作を、
「e.preventDefault()」を指定してキャンセルしているためです。
フリックで要素を動かす動作を自然に行うためには欠かせないことなのですが、
横いっぱいに広げたスライダーなどでは、これによってページのスクロールができない範囲が大きくなってしまうため、とても使い勝手の悪いものとなってしまいます。
今回は、この問題を解決する方法について考えてみましたので、
ご紹介いたします。
↓こちらがこの問題を解決したDEMOです(スマホやタブレットなどのタッチデバイスでのみ動きます)
↓問題解決前のDEMO
方法
今回の解決方法は、垂直方向から15度以内の方向にフリックした場合は、ページのスクロールを有効にして、
それ以上の角度の方向にフリックした場合はフリックを要素の移動を優先させて、ページのスクロールをキャンセルさせるというものです。
では次に実装方法をご紹介いたします。
jQuery(JavaScript)全体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | $(function(){ //スライダー調整 var tgElm = $(".slider"), elmWidth = 144, maxWidth = 720, colSize = 5, sldWidth = elmWidth * tgElm.find("li").size(); tgElm.css("width" , sldWidth + "px"); //フリック操作 $('.slider').on({ /* フリック開始時 */ 'touchstart': function(e) { this.touchX = event.changedTouches[0].pageX; this.slideX = $(this).position().left; this.touchY = event.changedTouches[0].pageY; //←縦方向のタッチ位置も取得 }, /* フリック中 */ 'touchmove': function(e) { var moveX = this.touchX - event.changedTouches[0].pageX, moveY = this.touchY - event.changedTouches[0].pageY, //←縦方向のタッチ位置も取得 moveRate = moveX / moveY; //←フリックした縦横の移動量の比率を計算 //↓垂直方向から15度以上の方向にフリックした場合のみ、ページのスクロールをキャンセル if(moveRate > Math.tan(15 * Math.PI/180)) { e.preventDefault(); } this.slideX = this.slideX - (this.touchX - event.changedTouches[0].pageX ); $(this).css({left:this.slideX}); this.accel = (event.changedTouches[0].pageX - this.touchX) * 5; this.touchX = event.changedTouches[0].pageX; }, /* フリック終了 */ 'touchend': function(e) { this.slideX += this.accel $(this).animate({left : this.slideX },200,'linear'); this.accel = 0; if (this.slideX > 0) { this.slideX = 0; $(this).animate({left:"0px"},500); } if (this.slideX < -(sldWidth - $(".box").width())) { this.slideX = -sldWidth; $(this).animate({left: -(sldWidth - $(".box").width()) + "px"},500); } } }); }); |
まず、以前ご紹介した方法では、「touchstart」のイベントのときに、タッチした場所の情報として横方向の座標であるXの値だけを取得していましたが、これに加えて、縦方向の座標であるYの座標も取得します。
1 2 3 4 5 6 7 8 9 10 | ・・・ //フリック操作 $('.slider').on({ /* フリック開始時 */ 'touchstart': function(e) { this.touchX = event.changedTouches[0].pageX; this.slideX = $(this).position().left; this.touchY = event.changedTouches[0].pageY; //←縦方向のタッチ位置も取得 }, ・・・ |
次に、「touchmove」のイベントでも同様に、
移動先の座標として横方向のX座標に加えて、縦方向のY座標も取得します。
取得できたら「touchstart」で取得した値と「touchmove」で取得した座標を元に、
フリックの方向を計算します。
そして、フリックの方向が垂直方向から15度以上の場合は「e.preventDefault()」でページのスクロールをキャンセルし、15度以内の場合はページのスクロールができるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ・・・ /* フリック中 */ 'touchmove': function(e) { var moveX = this.touchX - event.changedTouches[0].pageX, moveY = this.touchY - event.changedTouches[0].pageY, //←縦方向のタッチ位置も取得 moveRate = moveX / moveY; //←フリックした縦横の移動量の比率を計算 //↓垂直方向から15度以上の方向にフリックした場合のみ、ページのスクロールをキャンセル if(moveRate > Math.tan(15 * Math.PI/180)) { e.preventDefault(); } this.slideX = this.slideX - (this.touchX - event.changedTouches[0].pageX ); $(this).css({left:this.slideX}); this.accel = (event.changedTouches[0].pageX - this.touchX) * 5; this.touchX = event.changedTouches[0].pageX; }, ・・・ |
これでページのスクロールを邪魔せず、フリックによる操作が実装できました。
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE