「Swiper の自動再生が止まります...」との報告を受けました。

コードを確認すると間違いなく autoplay: true は設定されています。しかし、ブラウザで動作確認をすると、最初は正常に自動再生されるものの、ユーザーが少し操作すると止まってしまいます。

この記事では、Swiper の表面的な設定方法ではなく、Swiper の自動再生の仕組みを根本から理解し、確実に動作する実装方法。「なぜ自動再生が止まるのか」の本質の理解について解説します。

    なぜ「autoplay: true」だけでは足りないのか

    多くの開発者が陥る最初の誤解がここにあります。autoplay: true は「自動再生を有効にする」設定であって、「常に自動再生し続ける」設定ではありません。

    ブラウザの自動再生ポリシーが変わりました

    2018年以降、主要ブラウザは「自動再生ポリシー」を大幅に変更しました。Chrome 66、Safari 11.1、Firefox 66 から、ユーザーの明示的な操作なしに音声や動画の自動再生を制限するようになりました。

    この変更の背景には、ユーザー体験の向上があります。予期しない音声や動画の自動再生は、データ通信量の増加、バッテリー消費、注意力の散漫を引き起こすためです。

    Swiper のような UI コンポーネントも、この影響を受けています。特に以下のケースで自動再生が制限されます:

    • ページ読み込み直後(ユーザー操作前)
    • モバイルデバイスでの表示
    • 省電力モードが有効な環境

    ユーザー操作が必要な理由

    ブラウザが求める「ユーザー操作」とは具体的に何でしょうか。これを理解することで、なぜ自動再生が止まるのかが見えてきます。

    有効なユーザー操作

    • クリック、タップ
    • キーボード操作(エンターキー、スペースキーなど)
    • スワイプ、ドラッグ

    無効なユーザー操作

    • ページスクロール
    • マウスホバー
    • フォーカス移動のみ

    つまり、ユーザーが意図的に行う操作のみが「ユーザー操作」として認識されます。

    モバイルとデスクトップの違い

    さらに複雑なのは、モバイルとデスクトップで自動再生の挙動が異なることです。

    デスクトップブラウザ

    • 比較的寛容な自動再生ポリシー
    • タブがアクティブな間は継続しやすい

    モバイルブラウザ

    • より厳格な自動再生ポリシー
    • バックグラウンドに移ると即座に停止
    • バッテリー残量によっても制限

    Swiper が自動再生を停止する5つの条件

    Swiper の内部実装を詳しく調べると、自動再生が停止される条件が明確に見えてきます。

    1. ユーザーがスワイプした時

    これは最も一般的な停止条件です。Swiper は、ユーザーが手動でスライドを操作した瞬間に自動再生を停止します。

    // Swiper内部の処理(簡略化)
    swiper.on('touchStart', () => {
    if (swiper.autoplay.running) {
    swiper.autoplay.stop();
    }
    });
    

    この動作は UX の観点では正しいです。ユーザーが手動で操作しているのに、自動再生が続行されると混乱を招くからです。

    2. タブが非アクティブになった時

    ブラウザの Page Visibility API により、タブが非アクティブになると自動再生が停止されます。

    document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
    swiper.autoplay.stop();
    } else {
    swiper.autoplay.start();
    }
    });
    

    ただし、この復帰処理は確実ではありません。ブラウザによっては、タブを再度アクティブにしても自動的には再開されない場合があります。

    3. 省電力モードが有効な時

    モバイルデバイスの省電力モードでは、不要なアニメーションや定期処理が制限されます。Swiper の自動再生も例外ではありません。

    // バッテリー API による検出(実験的)
    if ('getBattery' in navigator) {
    navigator.getBattery().then(battery => {
    if (battery.level < 0.2) {
    // 省電力モードの可能性
    swiper.autoplay.stop();
    }
    });
    }
    

    4. Intersection Observer による可視性制御

    Swiper v8 以降では、Intersection Observer API を使用して、スライダーが画面外にある時の自動再生を制御しています。

    const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
    if (entry.isIntersecting) {
    swiper.autoplay.start();
    } else {
    swiper.autoplay.stop();
    }
    });
    });
    observer.observe(swiperContainer);
    

    これはパフォーマンス向上の観点では有効ですが、期待した動作と異なる場合があります。

    5. メモリ不足やパフォーマンス劣化

    JavaScriptエンジンのガベージコレクションやメモリ不足により、タイマー処理が不安定になることがあります。特に古いデバイスや、メモリを多く消費するページで発生しやすくなります。

    確実に動作する実装パターン

    これまでの分析を踏まえ、確実に動作する Swiper の自動再生実装を段階的に解説します。

    基本設定:必要最小限の実装

    const swiper = new Swiper('.swiper', {
    autoplay: {
    delay: 3000,
    disableOnInteraction: false,  // 重要:ユーザー操作後も継続
    pauseOnMouseEnter: true,      // UX配慮:ホバー時は停止
    },
    // 自動再生の継続性を高める設定
    watchSlidesProgress: true,
    observer: true,
    observeParents: true,
    // アクセシビリティ配慮
    a11y: {
    enabled: true,
    prevSlideMessage: '前のスライド',
    nextSlideMessage: '次のスライド',
    }
    });
    

    応用設定:ロバストな自動再生制御

    class RobustSwiperAutoplay {
    constructor(selector, options = {}) {
    this.swiper = new Swiper(selector, {
    ...options,
    autoplay: {
    delay: options.delay || 3000,
    disableOnInteraction: false,
    ...options.autoplay
    }
    });
    this.setupAutoplayControl();
    }
    setupAutoplayControl() {
    // タブの可視性変更を監視
    document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
    this.swiper.autoplay.stop();
    } else {
    // 遅延を入れて確実に再開
    setTimeout(() => {
    this.swiper.autoplay.start();
    }, 100);
    }
    });
    // ウィンドウフォーカス制御
    window.addEventListener('blur', () => {
    this.swiper.autoplay.stop();
    });
    window.addEventListener('focus', () => {
    setTimeout(() => {
    this.swiper.autoplay.start();
    }, 100);
    });
    // Intersection Observer による可視性制御
    const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
    if (entry.isIntersecting && entry.intersectionRatio > 0.1) {
    this.swiper.autoplay.start();
    } else {
    this.swiper.autoplay.stop();
    }
    });
    }, {
    threshold: 0.1
    });
    observer.observe(this.swiper.el);
    }
    // 外部からの制御インターフェース
    forceStart() {
    this.swiper.autoplay.start();
    }
    forceStop() {
    this.swiper.autoplay.stop();
    }
    }
    // 使用例
    const robustSwiper = new RobustSwiperAutoplay('.swiper', {
    delay: 4000,
    effect: 'fade'
    });
    

    トラブルシューティングのチェックリスト

    自動再生が期待通りに動作しない場合の診断手順をご紹介します:

    1. 基本設定の確認

    // デバッグ用:自動再生の状態を監視
    swiper.on('autoplayStart', () => {
    console.log('Autoplay started');
    });
    swiper.on('autoplayStop', () => {
    console.log('Autoplay stopped');
    });
    swiper.on('autoplayPause', () => {
    console.log('Autoplay paused');
    });
    

    2. ブラウザポリシーの確認

    // ユーザー操作の検出
    let userInteracted = false;
    document.addEventListener('click', () => {
    userInteracted = true;
    console.log('User interaction detected');
    }, { once: true });
    // 自動再生開始の遅延
    setTimeout(() => {
    if (userInteracted) {
    swiper.autoplay.start();
    } else {
    console.warn('Autoplay may be blocked by browser policy');
    }
    }, 1000);
    

    3. パフォーマンス状況の確認

    // メモリ使用量の監視(Chrome DevTools)
    if ('memory' in performance) {
    console.log('Memory usage:', performance.memory);
    }
    // フレームレートの監視
    let lastTime = performance.now();
    function checkFrameRate() {
    const now = performance.now();
    const fps = 1000 / (now - lastTime);
    lastTime = now;
    if (fps < 30) {
    console.warn('Low FPS detected:', fps);
    }
    requestAnimationFrame(checkFrameRate);
    }
    checkFrameRate();
    

    ユーザー体験を損なわない自動再生の設計

    技術的に動作する実装ができても、ユーザー体験を考慮しなければ意味がありません。

    アクセシビリティへの配慮

    自動再生は、アクセシビリティの観点で重要な考慮事項があります。

    const swiper = new Swiper('.swiper', {
    autoplay: {
    delay: 5000, // 十分な読取時間を確保
    disableOnInteraction: false,
    },
    // スクリーンリーダー対応
    a11y: {
    enabled: true,
    slideRole: 'group',
    slideLabelMessage: 'スライド {{index}} / {{slidesLength}}',
    }
    });
    // 停止・再開ボタンの実装
    const playPauseBtn = document.querySelector('.play-pause-btn');
    let isPlaying = true;
    playPauseBtn.addEventListener('click', () => {
    if (isPlaying) {
    swiper.autoplay.stop();
    playPauseBtn.textContent = '再生';
    playPauseBtn.setAttribute('aria-label', '自動再生を開始');
    } else {
    swiper.autoplay.start();
    playPauseBtn.textContent = '停止';
    playPauseBtn.setAttribute('aria-label', '自動再生を停止');
    }
    isPlaying = !isPlaying;
    });
    

    パフォーマンスの最適化

    自動再生がページ全体のパフォーマンスに悪影響を与えないよう配慮します。

    // 複数のスライダーがある場合の制御
    class SwiperManager {
    constructor() {
    this.swipers = [];
    this.activeSwiper = null;
    }
    addSwiper(swiper) {
    this.swipers.push(swiper);
    // 画面内に入った時のみ自動再生
    const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
    if (entry.isIntersecting) {
    this.setActiveSwiper(swiper);
    } else if (this.activeSwiper === swiper) {
    this.setActiveSwiper(null);
    }
    });
    });
    observer.observe(swiper.el);
    }
    setActiveSwiper(swiper) {
    // 他のスライダーを停止
    this.swipers.forEach(s => {
    if (s !== swiper) {
    s.autoplay.stop();
    }
    });
    // アクティブなスライダーを開始
    if (swiper) {
    swiper.autoplay.start();
    }
    this.activeSwiper = swiper;
    }
    }
    const manager = new SwiperManager();
    manager.addSwiper(swiper1);
    manager.addSwiper(swiper2);
    

    停止・再開のUX設計

    ユーザーが自動再生を制御できるインターフェースの設計例をご紹介します:

    .swiper-controls {
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 10;
    }
    .play-pause-btn {
    background: rgba(0, 0, 0, 0.7);
    color: white;
    border: none;
    border-radius: 50%;
    width: 44px;
    height: 44px;
    cursor: pointer;
    transition: background 0.3s ease;
    }
    .play-pause-btn:hover {
    background: rgba(0, 0, 0, 0.9);
    }
    .play-pause-btn:focus {
    outline: 2px solid #007bff;
    outline-offset: 2px;
    }
    
    // プログレスバーの実装
    class AutoplayProgress {
    constructor(swiper, progressElement) {
    this.swiper = swiper;
    this.progressElement = progressElement;
    this.setupProgress();
    }
    setupProgress() {
    let progressInterval;
    this.swiper.on('autoplayStart', () => {
    const delay = this.swiper.params.autoplay.delay;
    let progress = 0;
    progressInterval = setInterval(() => {
    progress += 100 / (delay / 10);
    this.progressElement.style.width = `${Math.min(progress, 100)}%`;
    if (progress >= 100) {
    clearInterval(progressInterval);
    }
    }, 10);
    });
    this.swiper.on('slideChangeTransitionStart', () => {
    clearInterval(progressInterval);
    this.progressElement.style.width = '0%';
    });
    this.swiper.on('autoplayStop', () => {
    clearInterval(progressInterval);
    });
    }
    }
    

    まとめ:

    この記事を通じて、Swiper の自動再生は単純な設定問題ではなく、ブラウザポリシー、ユーザー体験、パフォーマンスが複雑に絡み合った課題であることを理解していただけたと思います。

    重要なポイント

    1. autoplay: true は開始点に過ぎません - ブラウザの制限とユーザー操作の関係を理解しましょう
    2. 停止条件を把握しましょう - なぜ止まるのかを知ることで、適切な対策が打てます
    3. ロバストな実装を心がけましょう - 単一の設定ではなく、複数の制御を組み合わせます
    4. ユーザー体験を最優先にしましょう - 技術的に動作しても、UXが悪ければ意味がありません

    表面的な解決策を求めがちな現代の開発現場ですが、本質を理解することで初めて、確実に動作し、ユーザーに愛される実装ができます。次回 Swiper の自動再生で悩んだ時は、「なぜ止まるのか」から考えてみてください。その思考プロセスこそが、優れたフロントエンド開発者への第一歩です。

    Swiperのautoplay(自動再生)が止まらないようにする方法 はじめにSwiperの自動再生機能を使用する際、disableOnInteraction: falseを設定しても、時々スライドが止まってしまう問題に遭遇することがあります。この記事では、その原因と解決方法について解説します。問題の原因Swiperの自動再生が...  続きを読む