11.

JavaScript でスワイプ/ドラッグ実装(Touch / Pointer Events + Swiper / Hammer.js)

編集
この記事の要点
  • Touch Events (touchstart / touchmove / touchend) はスマホ専用。event.touches[0].clientX で座標取得
  • Pointer Events (pointerdown / pointermove / pointerup) はマウス・タッチ・ペンを統一して扱える 現代の推奨 API
  • CSS の touch-action: pan-y で縦スクロールだけ許可 → 横スワイプ判定を安定化
  • CSS のみで簡易スワイプ: scroll-snap-type: x mandatory + scroll-snap-align: start
  • 本格的に作るなら Swiper.js (カルーセル特化) / Hammer.js (ジェスチャ抽象化) を使う
  • preventDefault() を呼ぶ場合は { passive: false } 指定(Chrome 56+ 必須)

素の Touch Events で実装

最小構成のスワイプ判定:

const slider = document.getElementById('slider');
let startX = 0;
let startY = 0;
let dx = 0;

slider.addEventListener('touchstart', (e) => {
  startX = e.touches[0].clientX;
  startY = e.touches[0].clientY;
  dx = 0;
}, { passive: true });

slider.addEventListener('touchmove', (e) => {
  dx = e.touches[0].clientX - startX;
  const dy = e.touches[0].clientY - startY;

  // 横方向が支配的なら横スクロールとみなす
  if (Math.abs(dx) > Math.abs(dy)) {
    e.preventDefault(); // 縦スクロール抑制
    slider.style.transform = `translateX(${dx}px)`;
  }
}, { passive: false });

slider.addEventListener('touchend', () => {
  const THRESHOLD = 80;  // px
  if (dx > THRESHOLD) {
    goPrev();
  } else if (dx < -THRESHOLD) {
    goNext();
  } else {
    slider.style.transform = 'translateX(0)';  // 戻す
  }
});

Pointer Events で統一(推奨)

マウス・タッチ・ペンを 1 つのイベントで扱えます:

const el = document.getElementById('slider');
let startX = 0, dx = 0, dragging = false;

el.addEventListener('pointerdown', (e) => {
  dragging = true;
  startX = e.clientX;
  dx = 0;
  el.setPointerCapture(e.pointerId);  // ドラッグ中、要素外に出ても追従
});

el.addEventListener('pointermove', (e) => {
  if (!dragging) return;
  dx = e.clientX - startX;
  el.style.transform = `translateX(${dx}px)`;
});

el.addEventListener('pointerup', (e) => {
  if (!dragging) return;
  dragging = false;
  el.releasePointerCapture(e.pointerId);

  if (Math.abs(dx) > 80) {
    dx > 0 ? goPrev() : goNext();
  }
  el.style.transition = 'transform 0.3s';
  el.style.transform = 'translateX(0)';
  setTimeout(() => el.style.transition = '', 300);
});

CSS の touch-action でジェスチャを制御

意味
autoデフォルト。すべてのジェスチャを許可
noneすべて JS で制御(タップ含む)
pan-y縦スクロールのみブラウザに任せる(横スワイプを JS で)
pan-x横スクロールのみブラウザに任せる
manipulationパン・ピンチ可、ダブルタップズーム抑制(フォーム最適)
.slider {
  touch-action: pan-y;  /* 横スワイプは JS、縦スクロールはブラウザ */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;  /* iOS の長押しメニュー抑制 */
}

.slide {
  /* GPU アクセラレーション */
  will-change: transform;
  transform: translateZ(0);
}

CSS のみで作る簡易スライダー(scroll-snap)

1
2
3

これだけで PC・スマホ共通のスナップ式スライダーになります。JS なしで実装できるため、メンテナンス性が高い選択肢です。

ライブラリを使う(Swiper.js)

本格的なカルーセル / スワイプは Swiper.js がデファクトです:




Slide 1
Slide 2
Slide 3

Hammer.js(ジェスチャ抽象化)

import Hammer from 'hammerjs';

const hammer = new Hammer(document.getElementById('slider'));
hammer.on('swipeleft',  () => goNext());
hammer.on('swiperight', () => goPrev());
hammer.on('swipeup',    () => console.log('上スワイプ'));
hammer.on('swipedown',  () => console.log('下スワイプ'));

// ピンチイン・アウト
hammer.get('pinch').set({ enable: true });
hammer.on('pinch', (e) => {
  console.log('scale:', e.scale);
});

よくあるハマりどころ

  • passive リスナー: Chrome 56+ では touchmoveデフォルト passivepreventDefault() したい場合は { passive: false } 必須
  • iOS Safari の慣性スクロール: -webkit-overflow-scrolling: touch がないとカクつく
  • クリックとスワイプの誤判定: 移動量が小さい場合(< 5px)はクリックとして扱う閾値を設ける
  • iframe 内のタッチ: 親ページの touch-action が影響する場合がある
  • iOS の長押しメニュー: -webkit-touch-callout: none で抑制

FAQ

Q: マウスとタッチ両対応で 1 つのコードにしたい
A: Pointer Events を使えば 1 セットで両方扱えます。IE 非対応ですが現代では問題なし。

Q: スワイプとスクロールがケンカする
A: touch-action: pan-y でスクロール方向を明示し、JS 側で Math.abs(dx) > Math.abs(dy) の判定を入れます。

Q: Swiper と React / Vue の組み合わせは?
A: 公式に swiper/react / swiper/vue パッケージがあり、それぞれ専用コンポーネントとして使えます。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 導入方法
  2. 文法
  3. HTML/CSS 操作・制御
  4. 要素の取得
  5. 値の取得
  6. 値と要素の追加
  7. 値と要素の削除
  8. 子要素の削除
  9. 要素のコピー
  10. Ajax
  11. 項目をタッチ/クリックしてスライドさせる方法
  12. テキスト/セレクトボックス/ラジオボタン変更時のイベント
  13. パスワードを一時的に表示させる方法
  14. $(document).ready(function() { ...
  15. セレクトボックスにオプションを追加する方法