フリックでタブの切り替えをしたい!って要件を本当よく見ますよね。
スマホWebってそれやっても気持ちいい動きにするのが手間だったので記事にしてみます。
難しいポイント
- イベントが縦スクロールと混在してしまいがち。
- フリックするとページ戻りが発生する。
取るべきアプローチ
- フリックの角度に応じて調整する。
- どれぐらい動かしたら処理するのかを調整する。
フリックの実装
上の二点のアプローチを自前で実装してもいいのですが、
hammer.jsが優秀なので使います。
指の動きのベクトルを取得できます。
hammerの使い方
シンプルです。
var mc = new Hammer(document.getElementById("swiper"));
mc.on("swipe", function(e){処理});
これでswipeされた時の処理を埋め込むことができます。
引数の中はこんな感じで動きがかなり整理されて入ってきます!
dditionalEvent:"panleft"
angle:180
changedPointers:Array[1]
deltaTime:148
deltaX:-13
deltaY:0
direction:2
distance:13
eventType:2
isFinal:false
isFirst:false
maxPointers:1
offsetDirection:2
overallVelocity:-0.08783783783783784
overallVelocityX:-0.08783783783783784
overallVelocityY:0
pointerType:"touch"
pointers:Array[1]
rotation:0
scale:1
timeStamp:1458114648821
type:"pan"
velocity:-0.06870229007633588
velocityX:-0.06870229007633588
velocityY:0
panを使う
iPhoneは大きく横に動かすとページ戻りと競合するので、
短めで発火するpanにしました。
アニメーション
実装方法はなんでもいいのですが、css3で動かすのが良いです。
今回は jquery.transit を使いました。
コード
var duringAnimation = false;//アニメーション中のロック用フラグ
var mc = new Hammer(document.getElementById("swiper"));
mc.on("pan", function(e){
var viewItem = $(e.target).closest(".view-mode");
var positions = $(".position > li");
var index = $(viewItem).index();
var length = $(".view-mode").length;
var nextItem;
var nextIndex;
var direction;
var range = 60;//判定する角度
var easing = 'ease';
if (e.distance < 70) return true;
//範囲外だったら処理しない。
if ( !(e.angle > -180 - range && e.angle < -180 + range) && !(e.angle < range && e.angle > -range)){
return true;
}
//方向
direction = (e.additionalEvent === "panleft") ? "left":"right";
var step = (direction === "left") ? 1:-1;
nextIndex = index + step;
if (nextIndex > length-1)
{
nextIndex = 0;
}else if (nextIndex < 0){
nextIndex = length-1;
}
nextItem = $(".view-mode")[nextIndex];
$(positions).removeClass("active");
$(positions[nextIndex]).addClass("active");
//$("#position ul").animate({left: 0}, 100, "easeOutExpo");
$(window).scrollTop(0);
if (duringAnimation === false) {
duringAnimation = true;
$(nextItem).css({"left":"100%", "display":"block"}).transition({left: "0"}, 300, easing);
$(viewItem).transition({left: (direction === "left") ? "-100%":"100%" }, 300, easing,function () {
$(viewItem).css({"position": "absolute", "display": "none"});
$(nextItem).css({"margin-top": "0", "position": "relative", "display": "block"});
duringAnimation = false;
});
}
});