読者です 読者をやめる 読者になる 読者になる

Develop and Design Note

フロントエンドなデザイナーの覚書

プラグイン使わずにjQueryだけでスワイプ対応のカルーセル作ってみた。

以前、ボタンで動かすカルーセルは作成したのですが(レスポンシブWebデザインでカルーセルを実装してみた - Web Design Note)、これをスマートフォンのスワイプに対応させました。
JavaScriptプラグインを使えば早いのですが、勉強のため、今回はプラグインを使わずにjQueryだけでスワイプ対応してみました。

実際に作成したページはこちら
作成したJavaScriptこちらの「6)スワイプでスライド」の部分です。

そもそも、タッチイベントを使ったことが無い

といことで、最初はタッチイベントの使い方を調べるところから始まりました。最も参考になった記事はこちらです。

基本的な書き方としては、 以下の3つのタッチイベントをbindメソッドを使って、実行したい関数と紐づけます。

  • touchstart(タッチした瞬間)
  • touchmove(タッチ中)
  • touchend(指を離した後)

逆に、これ以外のイベントは書く必要がないというわけです。

指に追従させて要素を移動させる、スワイプ中の処理

スワイプ中に実行したいアクションは touchmoveにbindした関数として記述します。ここで一番やりたいことは、「指の動きに合わせて、画像を移動させる」こと、つまり、「指の横の移動距離と同じ距離だけ、画像を移動させる」です。
なので、まず「指の横の移動距離」を、「最初にタッチしたX座標」と「移動中のX座標」の差分として取得します。ちなみに、「指を離した位置のX座標」は取得できません(touchendの関数として無効)。

「最初にタッチしたX座標」はtouchstartの関数に書く必要があります。

$(modalFigureWrWr).bind('touchstart', function() {
	startX = event.changedTouches[0].pageX;
	...
});

「移動中のX座標」はtouchmoveの関数で取得できるので、この段階で「指の横の移動距離」(diffX)を取得することができます。

$(modalFigureWrWr).bind('touchmove', function(e) {
	endX = event.changedTouches[0].pageX;
	diffX = Math.round(startX - endX);// 差分
	absX = Math.abs(diffX);// 絶対値
	...
});

こうして初めて、画像(正確には画像を内包する親要素)を移動させる記述を書くことができます。

if (diffX > 0) {// 左スワイプで左に追従
	$(modalFigureWr).css('margin-left',-+modalFigureWidth+-+absX+'px');
} else if (diffX < 0) {// 右スワイプで右に追従
	$(modalFigureWr).css('margin-left',-+modalFigureWidth+absX+'px');
};

指を離した瞬間に要素をスライドさせるスワイプ後の処理

次にやりたいことは、「指を離した瞬間にカルーセルを回す」ことなので、touchendの関数に「カルーセルを回す」処理を書きます。
この処理は、ソースの「5)左右ボタンでスライド」に記述した処理とほぼ同じです。ただし、「スワイプの距離が画像の半分未満だったときは、カルーセルを回さない」という処理を書く必要があります。

}else if(absX < imgHalfWidth){//スライド距離が半分未満
	$(modalFigureWr).animate({marginLeft: -+modalFigureWidth+'px'},200,'linear')
};

はまったところ

transformプロパティの値はマトリックス関数になる

今回、スライド処理はmargin-leftプロパティのネガティブマージンで実装していますが、leftやtransformプロパティで実装する方法もあります。
ですが、transformの場合、取得される値が「matrix(1, 0, 0, 1, 0, 0)」のようなマトリックス(行列)関数になるので、そのままでは数値として取り出せず、animateメソッドと上手く連携できなかったりします(できる方法あれば教えてください!)。
ただ、transformはtransitionと一緒に利用することでアニメーションを実現し、スマホででも滑らかに動かせる、などメリットがあるようです。

touchmoveにbindした関数は指を動かしている間、実行し続ける

if (diffX > 0) {// 左スワイプで左に追従
	$(modalFigureWr).css('margin-left',-+modalFigureWidth+-+absX+'px');
}

最初、この記述を、

if (diffX > 0) {// 左スワイプで左に追従
	$(modalFigureWr).css('margin-left',-+parseInt($(modalFigureWr).css("margin-left"))+-+absX+'px');
}

のように書いていました。
つまり、margin-leftの値を固定値ではなく、「常にmargin-left値を取得して出力する」という書き方でした。なので、指を動かしている間は次々にmargin-left値が変化するのですが、その変化してく値を出力してしまい、指の動きよりも早くスライドしてしまう現象がおきてしまいました。
変数に置き換えるだけの書き直しでしたが、意味は全く違うのですね。

ルーセルのボタンを押したときとスワイプした時では、スライドの方向が逆になる

これはJavaScriptの問題といよりかは、想像力の問題かも。
ルーセルの正しい動きとしては、「左ボタンを押したときに画像は右にスライド」しますが、「左スワイプする時は左にスライド」します。ボタン操作とスワイプとで、スライドする方向が逆になるのです。
これを頭の中で勝手に「左スワイプで右にスライドする」と思い込んでいて、実装したソースと実際の動きが逆だったので、頭がこんがらがったわけです。ややこしい。
そんなわけで、悪戦苦闘しながらもスワイプ対応のカルーセルが無事実装できました。(ソースにやけにコメントが多くなってしまったのは、悪戦苦闘の名残です。。)