1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

サムネ付きスライドショー作りました

1
Last updated at Posted at 2022-08-21

class化しました

objectの要素を入れ替えて汎用的に使えるようclass化しました。
また、手動スライドと自動スライドを統合して、autoPlayでtrueかfalseで自動と手動を選べるようにしました。

touchイベントに関してはclassの外で関数にしましたが、これもclassの中に入れた方が良かったかなと後から思ったりしました。classを使うのは初めてなので今は勉強中ということで。
touchイベントとclickイベントの制御が難しかったです。

cssは一部変更を加えましたが、ほぼ前バージョンと同じです。css次第でいろんなレイアウトができるかなと。
htmlは同じものを使用してるので、jsだけ更新します。

以前のバージョンは下記に移しました。

普通のスライド

サンプルサイト>

slide01.png

myslide.js
$(function () {
  //要素取得object
  const slideEl = {
    panelImg: $('.panelImg'),//画像を二つ並べるところ
    next: $('#next'),
    prev: $('#prev'),
    thumbID: '#thumb',//タッチイベント除外用
    thumb: $('#thumb li'),//要素格納
    act: 'act',//マーカー
    autoPlay: true,//自動再生ON/OFF
    wait: 3000,//自動再生スピード
    speed: 800,//スライド待機時間
    itemW: $('#slidePanel'),//1枚の画像幅スライドの基準
    pointBreak: 768,//レスポンシブ切替幅 
    panel: $('.panel'),//タッチイベント領域
    swipeThreshold: 20// スワイプ判定距離(px)
  }

  //class宣言
  class Slide {
    constructor() {
      this.imgW = 0;
      this.panelImg = slideEl.panelImg;
      this.thumb = slideEl.thumb;
      this.act = slideEl.act;
      this.next = slideEl.next;
      this.prev = slideEl.prev;
      this.itemW = slideEl.itemW;
      this.autoPlay = slideEl.autoPlay;
      this.pointBreak = slideEl.pointBreak;
      this.click = true;
      this.timerID = null;
    }

    slideW() {
      if (window.innerWidth > this.pointBreak) {
        this.imgW = this.itemW.width();
      } else {
        this.imgW = $(window).width();
      }

      // エラーハンドリング追加
      if (!this.imgW || this.imgW === 0) {
        console.error('Failed to calculate slide width');
        // デフォルト値を設定してクラッシュを防ぐ
        this.imgW = $(window).width(); // または適切なフォールバック値
        return false; // エラーを示すためにfalseを返す
      }
      return true;  // 成功時にtrueを返す
    }

    // タイマー管理を統一(クラスのメソッドとして)
    clearTimer() {
      if (this.timerID) {
        clearTimeout(this.timerID);
        this.timerID = null;
      }
    }

    //次へ
    nextSlide() {
      let nextImg, cloneImg;
      if (!this.click) return; // 早期リターン
      this.click = false;
      //カルーセルにする為、最後のliになったら最初のliを指定
      if (this.thumb.last().hasClass(this.act)) {
        nextImg = this.thumb.first();
      } else {
        nextImg = this.thumb.filter('.' + this.act).next();
      }
      //要素クローン
      cloneImg = nextImg.children().clone();
      this.panelImg.append(cloneImg);
      //actの付替え
      nextImg.addClass(this.act).siblings().removeClass(this.act);
      //スライド
      this.panelImg.stop().animate({ 'margin-left': this.imgW * -1 }, slideEl.speed,
        () => {
          this.panelImg.children().first().remove();
          this.panelImg.css('margin-left', 0);
          //スライドが終わったらクリックOK
          this.click = true;
        });
    }

    //前へ
    prevSlide() {
      let prevImg, cloneImg;
      if (!this.click) return; // 早期リターン
      this.click = false;
      //最初のliになったら最後のliを指定
      if (this.thumb.first().hasClass(this.act)) {
        prevImg = this.thumb.last();
      } else {
        prevImg = this.thumb.filter('.' + this.act).prev();
      }
      //要素クローン
      cloneImg = prevImg.children().clone();
      this.panelImg.prepend(cloneImg).css('margin-left', this.imgW * -1);
      prevImg.addClass(this.act).siblings().removeClass(this.act);
      //スライド
      this.panelImg.stop().animate({ 'margin-left': 0 }, slideEl.speed,
        () => {
          this.panelImg.children().last().remove();
          //スライドが終わったらクリックOK
          this.click = true;
        });
    }

    slideStart() {
      //自動スライド
      if (this.autoPlay === true) {
        this.timerID = setTimeout(() => {
          this.nextSlide();
          this.slideStart();
        }, slideEl.wait);
      }
    }

    nextClick() {
      //off()で重複防止
      this.next.off('click').on('click', () => {
        this.clearTimer();
        this.nextSlide();
        if (this.autoPlay === true) {
          this.slideStart();
        }
      });
    }

    prevClick() {
      this.prev.off('click').on('click', () => {
        this.clearTimer();
        this.prevSlide();
        if (this.autoPlay === true) {
          this.slideStart();
        }
      });
    }

    //サムネイルクリック
    clickThumb() {
      // off()で重複防止
      this.thumb.off('click').on('click', (e) => {
        // タッチ中の場合はクリックイベントを無視
        if (isTouching) {
          e.preventDefault();
          return false;
        }

        this.clearTimer();
        if (!this.click) return; // 早期リターン

        this.click = false;
        let point = this.thumb.index(e.currentTarget);

        this.thumb.eq(point)
          .addClass(this.act)
          .siblings()
          .removeClass(this.act);

        let imgNum = this.thumb.eq(point);
        this.panelImg.html(imgNum.html());

        if (this.autoPlay === true) {
          this.slideStart();
        }
        this.click = true;
      });
    }

    init() {
      if (!this.slideW()) {
        console.error('Slide initialization failed');
        return;
      }
      this.clickThumb();
      this.nextClick();
      this.prevClick();
      this.slideStart();
    }
  }

  const slide = new Slide();
  slide.init();

  // リサイズ処理
  let resizeTimer;
  $(window).on('resize', () => {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(() => {
      slide.slideW();
    }, 200);
  });

  //サムネイルのaリンク無効化、ページに飛んでしまうのを防止
  //.is()メソッド(要素がセレクタにマッチするか判定)
  if (slideEl.thumb.children().is('a')) {
    $('a', slideEl.thumb).on('click', function () {
      return false;
    });
  }


  //touchイベント
  /* 変数宣言 */
  let moveX, posiX;
  let isTouching = false; // タッチ中フラグを追加

  /* 指が触れたか検知 */
  // サムネイル領域を除外
  slideEl.panel.on('touchstart', function (e) {
    // サムネイルをタッチした場合は無視
    if ($(e.target).closest(slideEl.thumbID).length > 0) {
      return;
    }
    start_check(e);
  });

  /* 指が動いたか検知 */
  slideEl.panel.on('touchmove', function (e) {
    if ($(e.target).closest(slideEl.thumbID).length > 0) {
      return;
    }
    move_check(e);
  });

  /* 指が離れたか検知 */
  slideEl.panel.on('touchend', function (e) {
    if ($(e.target).closest(slideEl.thumbID).length > 0) {
      return;
    }
    end_check(e);
  });

  // サムネイルのタッチイベント対応
  slideEl.thumb.off('touchend').on('touchend', (e) => {
    if (moveX !== '') return; // スワイプ中は無視

    e.preventDefault(); // デフォルトのクリックイベントを防止
    $(e.currentTarget).trigger('click'); // 手動でクリックイベントを発火
  });

  //タッチ開始時の処理
  function start_check(e) {
    isTouching = true; // タッチ中フラグをON
    //タイマーストップ
    slide.clearTimer();
    /* 現在の座標取得 */
    posiX = getX(e);
    /* 移動距離状態を初期化 */
    moveX = '';
  }

  //スワイプ中の処理
  function move_check(e) {
    if (!isTouching) return; // タッチ中でなければ何もしない
    e.preventDefault();// スクロール防止
    if (posiX - getX(e) > slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
    {
      /* 右→左と判断 */
      moveX = 'left';
    } else if (posiX - getX(e) < -slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
    {
      /* 左→右と判断 */
      moveX = 'right';
    }
  }

  // タッチイベントの end_check 関数
  //指が離れた時の処理
  function end_check(e) {
    if (!isTouching) return;
    if (!slide.click) {
      isTouching = false; // フラグをリセット
      return;
    }

    if (moveX === 'left') {
      slide.nextSlide();
    }
    else if (moveX === 'right') {
      slide.prevSlide();
    }

    // 自動再生を再開(クラスのメソッドを使用)
    if (slideEl.autoPlay === true) {
      slide.slideStart();
    }

    // タッチ終了後、少し遅延してからフラグをリセット
    // これによりclick イベントとの競合を防ぐ
    setTimeout(() => {
      isTouching = false;
    }, 100);
  }

  function getX(e) {
    if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]) {
      return e.originalEvent.touches[0].pageX;
    }
    return 0;
  }

});

横幅100% 3枚並び自動スライド

サンプルサイト>

slide02.png

myslide_timer_wide.js
$(function () {
  //要素取得object
  const slideEl = {
    panelImg: $('.panelImg'),
    thumb: $('#thumb li'), //要素格納
    thumbID: '#thumb', //タッチイベント除外用に使う
    next: $('#next'),
    prev: $('#prev'),
    act: 'act', // マーカー
    speed: 800, //スライド速度
    wait: 3000, //スライド間隔
    itemW: $('.panel'), //1枚の画像幅スライドの基準
    panel: $('.panel'), //タッチイベント領域
    swipeThreshold: 20 // スワイプ判定距離(px)
  }

  class WideSlide {
    constructor() {
      this.panelImg = slideEl.panelImg;
      this.thumb = slideEl.thumb;
      this.next = slideEl.next;
      this.prev = slideEl.prev;
      this.act = slideEl.act;
      this.speed = slideEl.speed;
      this.wait = slideEl.wait;
      this.imgW = 0;
      this.itemW = slideEl.itemW;
      this.imageNum = slideEl.thumb.length;//画像枚数
      this.click = true;
      this.timerID = null;
    }

    slideW() {
      this.imgW = this.itemW.width();

      // エラーハンドリング追加
      if (!this.imgW || this.imgW === 0) {
        console.error('Failed to calculate slide width');
        // デフォルト値を設定してクラッシュを防ぐ
        this.imgW = $(window).width(); // または適切なフォールバック値
        return false; // エラーを示すためにfalseを返す
      }
      return true;  // 成功時にtrueを返す
    }

    // タイマー管理を統一(クラスのメソッドとして)
    clearTimer() {
      if (this.timerID) {
        clearTimeout(this.timerID);
        this.timerID = null;
      }
    }

    //次へ
    nextSlide() {
      let nextImg, cloneImg;
      //カルーセルにする為、最後のliになったら最初のliを指定
      if (this.click === true) {
        this.click = false;
        if (this.thumb.last().hasClass(this.act)) {
          nextImg = this.thumb.first();
        } else {
          nextImg = this.thumb.filter('.' + this.act).next();
        }

        if (this.thumb.eq(this.thumb.length - 2).hasClass(this.act)) {
          //要素クローン
          cloneImg = this.thumb.first().children().clone();
        } else {
          //要素クローンは二つ先を取得
          cloneImg = nextImg.next().children().clone();
        }
        this.panelImg.append(cloneImg);
        //actの付替え
        nextImg.addClass(this.act).siblings().removeClass(this.act);
        //スライド
        this.panelImg.stop().animate({ 'margin-left': this.imgW * -2 }, this.speed,
          () => {
            this.panelImg.children().first().remove();
            this.panelImg.css('margin-left', this.imgW * -1);
            this.click = true;
          });
      }
    }

    slideStart() {
      this.timerID = setTimeout(() => {
        this.nextSlide();
        this.slideStart();
      }, this.wait);
    }

    nextClick() {
      this.next.off('click').on('click', () => {
        this.clearTimer();
        this.nextSlide();
        this.timerID = setTimeout(() => { this.slideStart(); }, this.wait);
      });
    }

    //前へ
    prevSlide() {
      let prevImg, cloneImg;
      if (this.click === true) {
        this.click = false;
        //最初のliになったら最後のliを指定
        if (this.thumb.first().hasClass(this.act)) {
          prevImg = this.thumb.last();
        } else {
          prevImg = this.thumb.filter('.' + this.act).prev();
        }

        if (this.thumb.eq(1).hasClass(this.act)) {
          //要素クローン
          cloneImg = this.thumb.last().children().clone();
        } else {
          //要素クローン
          cloneImg = prevImg.prev().children().clone();
        }

        this.panelImg.prepend(cloneImg).css('margin-left', this.imgW * -2);
        prevImg.addClass(this.act).siblings().removeClass(this.act);
        //スライド
        this.panelImg.stop().animate({ 'margin-left': this.imgW * -1 }, this.speed,
          () => {
            this.panelImg.children().last().remove();
            //スライドが終わったらクリックOK
            this.click = true;
          });
      }
    }

    prevClick() {
      this.prev.off('click').on('click', () => {
        this.clearTimer();
        this.prevSlide();
        this.timerID = setTimeout(() => { this.slideStart(); }, this.wait);
      });
    }

    thumbClick() {
      //サムネイルクリック
      let imgNum, imgPrev, imgNext;
      this.thumb.off('click').on('click', (e) => {
        this.clearTimer();
        if (!this.click) return; // 早期リターン
        this.click = false; // 連打防止

        // タッチ中の場合はクリックイベントを無視
        if (isTouching) {
          e.preventDefault();
          this.click = true; // フラグをリセット
          return false;
        }

        let point = this.thumb.index(e.currentTarget);
        //クリックした場所を検索
        this.thumb.eq(point).addClass(this.act).siblings().removeClass(this.act);

        switch (point) {
          case this.imageNum - 1:
            imgPrev = this.thumb.eq(this.thumb.length - 2);
            imgNum = this.thumb.last();
            imgNext = this.thumb.first();
            break;
          case 0:
            imgPrev = this.thumb.last();
            imgNum = this.thumb.first();
            imgNext = this.thumb.eq(1);
            break;
          default:
            imgPrev = this.thumb.eq(point - 1);
            imgNum = this.thumb.eq(point);
            imgNext = this.thumb.eq(point + 1);
        }

        this.panelImg.html(imgPrev.html() + imgNum.html() + imgNext.html());
        // フラグをリセット
        this.click = true;
        // タイマーを再開
        this.timerID = setTimeout(() => { this.slideStart(); }, this.wait);
      });
    }

    init() {
      if (!this.slideW()) {
        console.error('Slide initialization failed');
        return;
      }
      this.slideStart();
      this.nextClick();
      this.prevClick();
      this.thumbClick();
    }

  }

  const slide = new WideSlide();
  slide.init();

  //リサイズ
  // リサイズのデバウンス追加
  let resizeTimer;
  $(window).on('resize', function () {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(() => {
      slide.slideW();
    }, 200);
  });

  //サムネイルのaリンク無効化、ページに飛んでしまうのを防止
  //.is()メソッド(要素がセレクタにマッチするか判定)
  if (slideEl.thumb.children().is('a')) {
    $('a', slideEl.thumb).on('click', function () {
      return false;
    });
  }


  //touchイベント
  /* 変数宣言 */
  let moveX, posiX;
  let isTouching = false; // タッチ中フラグを追加

  /* 指が触れたか検知 */
  // サムネイル領域を除外
  slideEl.panel.on('touchstart', function (e) {
    // サムネイルをタッチした場合は無視
    if ($(e.target).closest(slideEl.thumbID).length > 0) {
      return;
    }
    start_check(e);
  });

  /* 指が動いたか検知 */
  slideEl.panel.on('touchmove', function (e) {
    if ($(e.target).closest(slideEl.thumbID).length > 0) {
      return;
    }
    move_check(e);
  });

  /* 指が離れたか検知 */
  slideEl.panel.on('touchend', function (e) {
    if ($(e.target).closest(slideEl.thumbID).length > 0) {
      return;
    }
    end_check(e);
  });

  //タッチ開始時の処理
  function start_check(e) {
    isTouching = true; // タッチ中フラグをON
    //タイマーストップ
    slide.clearTimer();
    /* 現在の座標取得 */
    posiX = getX(e);
    /* 移動距離状態を初期化 */
    moveX = '';
  }

  //スワイプ中の処理
  function move_check(e) {
    if (!isTouching) return; // タッチ中でなければ何もしない
    e.preventDefault();// スクロール防止
    if (posiX - getX(e) > slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
    {
      /* 右→左と判断 */
      moveX = 'left';
    } else if (posiX - getX(e) < -slideEl.swipeThreshold) // swipeThreshold以上移動でスワイプと判断
    {
      /* 左→右と判断 */
      moveX = 'right';
    }
  }

  //指が離れた時の処理
  function end_check(e) {
    if (!isTouching) return;
    if (!slide.click) {
      isTouching = false; // フラグをリセット
      return;
    }

    if (moveX === 'left') {
      slide.nextSlide();
    }
    else if (moveX === 'right') {
      slide.prevSlide();
    }

    // 自動再生を再開
    slide.timerID = setTimeout(() => { slide.slideStart(); }, slide.wait);

    // タッチ終了後、少し遅延してからフラグをリセット
    // これによりclick イベントとの競合を防ぐ
    setTimeout(() => {
      isTouching = false;
    }, 100);
  }

  function getX(e) {
    if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]) {
      return e.originalEvent.touches[0].pageX;
    }
    return 0;
  }
});

モーダルスライド class ver.

サンプルサイト>

modal.jpg

myslide_modal.js
//touchイベントとモーダル
$(function () {

  const modalItem = {
    next: $('#next'), //次へボタン
    prev: $('#prev'), //前へボタン  
    act: 'act', //アクティブクラス
    thumb: $('#thumb li'), //サムネイルリストli
    modalBg: $('#overlay'), //モーダル背景
    close: $('#close_modal'), //閉じるボタン
    modal: $('.changeItem'), //モーダルコンテンツ
  };

  class MyModal {
    constructor(item) {
      this.thumb = item.thumb;
      //次へ、前へボタンがある場合のみ設定
      if (item.next && item.prev) {
        this.next = item.next;
        this.prev = item.prev;
      } else {
        this.next = null;
        this.prev = null;
      }
      this.act = item.act;
      this.modal = item.modal;
      this.close = item.close;
      this.modalBg = item.modalBg;
      //連打クリック禁止条件
      this.click = true;
    }

    //サムネイルクリック
    clickModal() {
      this.thumb.on('click', (e) => {
        if (!this.click) return; // 早期リターン
        this.click = false;
        let point = this.thumb.index(e.currentTarget);
        //e.currentTargetでクリックした要素を取得、thisと同じ作用
        this.thumb.eq(point)
          .addClass(this.act)
          .siblings()
          .removeClass(this.act);

        let imgNum = this.thumb.eq(point);
        this.modal.html(imgNum.html());
        this.modalBg.stop().fadeIn(800, () => {
          this.click = true;
          // fadeIn完了後にナビを更新
          if (this.next !== null && this.prev !== null) {
            this.navChange();
          }
        });
      });
    }

    //閉じる
    closeModal() {
      this.close.on('click', () => {
        this.modalBg.stop().fadeOut(600);
      });
    }

    //ナビの処理
    navChange() {
      if (this.thumb.last().hasClass(this.act)) {
        this.next.hide();
        this.prev.show();
      } else if (this.thumb.first().hasClass(this.act)) {
        this.prev.hide();
        this.next.show();
      } else {
        this.prev.show();
        this.next.show();
      }
    }

    //次へ
    nextSlide() {
      const classAct = this.thumb.filter('.' + this.act);
      let nextImg = classAct.next();
      if (nextImg.length === 0) return; // nextImgが空の場合
      this.modal.html(nextImg.html());
      nextImg.addClass(this.act).siblings().removeClass(this.act);
    }
    //前へ
    prevSlide() {
      const classAct = this.thumb.filter('.' + this.act);
      let prevImg = classAct.prev();
      if (prevImg.length === 0) return; // prevImgが空の場合
      this.modal.html(prevImg.html());
      prevImg.addClass(this.act).siblings().removeClass(this.act);
    }

    clickNext() {
      if (this.next !== null && this.prev !== null) {
        this.next.on('click', () => {
          this.nextSlide();
          this.navChange();
        });
      }
    }

    clickPrev() {
      if (this.next !== null && this.prev !== null) {
        this.prev.on('click', () => {
          this.prevSlide();
          this.navChange();
        });
      }
    }

    //実行関数まとめる
    behavior() {
      this.clickModal();
      this.closeModal();
      this.clickNext();
      this.clickPrev();
    }
  }

  //モーダル本体の要素 呼び出しインスタンス
  const myModal = new MyModal(modalItem);
  myModal.behavior();

  //touchイベント
  /** 変数宣言 */
  let moveX, posiX

  /** 指が触れたか検知 */
  modalItem.modalBg.on('touchstart', start_check);

  /** 指が動いたか検知 */
  // パッシブリスナー対応
  modalItem.modalBg.on('touchmove', { passive: false }, move_check);

  /** 指が離れたか検知 */
  modalItem.modalBg.on('touchend', end_check);

  //タッチ開始時の処理
  function start_check(e) {
    /** 現在の座標取得 */
    posiX = getX(e);
    /** 移動距離状態を初期化 */
    moveX = '';
  }

  //スワイプ中の処理
  function move_check(e) {
    e.preventDefault();// スクロール防止
    if (posiX - getX(e) > 20) {
      if (modalItem.thumb.last().hasClass(modalItem.act)) {
        moveX = 'stop';
      } else {
        moveX = 'left';
      }
    } else if (posiX - getX(e) < -20) {
      if (modalItem.thumb.first().hasClass(modalItem.act)) {
        moveX = 'stop';
      } else {
        moveX = 'right';
      }
    }
  }

  //指が離れた時の処理
  function end_check() {
    if (moveX === 'left') {
      myModal.nextSlide();
      myModal.navChange();
    }
    else if (moveX === 'right') {
      myModal.prevSlide();
      myModal.navChange();
    }
  }

  function getX(e) {
    //横方向の座標を取得
    if (e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]) {
      return e.originalEvent.touches[0].pageX;
    }
    return 0;
  }
});

前バージョンの記事

便利なプラグインがたくさんあるスライドショーですが、できれば自作のものを使いたいと思いjsで開発しました。

ベースを作って自分のストック用として手動、自動、モーダルと1セット揃えました。
この際、逃げ腰だったタッチイベントにも挑戦。

手動スライド

touch.html
    <div id="slidePanel">
    <!-- 表示領域 -->
      <div class="panel">
        <figure class="panelImg">
        <!-- デフォルトに画像一つ置いておく -->
          <img src="./image/slide1.jpg" alt="">
        </figure>
      </div>
    <!-- prevボタンとnextボタン -->
      <div id="prev"><img src="./image/prev.svg" alt="前へ"></div>
      <div id="next"><img src="./image/next.svg" alt="次へ"></div>
    </div>
    <!-- //.slidePanel -->

    <!-- サムネイル スライド画像を格納 -->
    <ul id="thumb">
      <li class="thumbLi act"><img src="./image/slide1.jpg" alt=""></li>
      <li class="thumbLi"><img src="./image/slide2.jpg" alt=""></li>
      <li class="thumbLi"><img src="./image/slide3.jpg" alt=""></li>
      <li class="thumbLi"><img src="./image/slide4.jpg" alt=""></li>
      <li class="thumbLi"><img src="./image/slide5.jpg" alt=""></li>
      <li class="thumbLi"><img src="./image/slide6.jpg" alt=""></li>
    </ul>

普通だったらスライドのブロックに画像をliで書き出しますが、サムネイルのliに格納して非表示にしました。
actクラスでアクティブになっている要素と紐付けし、actが付与されたli直下のimgを取得します。
サムネをクリックした時、この方が分かりやすいと思ったもので。

・下準備 サイズ設定

myslide.js
//スライド一つ分の長さを取得
 let imgW;
 const panelImg = $('.panelImg');
  //スライド幅のレスポンシブ
  function slideW(){
    if(window.innerWidth > 768) {
      imgW = $('img',panelImg).width();
    } else {
      imgW = $(window).width();
    }
  }
  slideW();
  //次の画像をセットするので幅を追加しておく
  panelImg.width(imgW * 2);
  //リサイズ
  $(window).on('resize',function(){
    slideW();
    panelImg.width(imgW * 2);
  });

・nextボタンの実行
スライド中、クリック連打を防ぐ為if分で制御しました。
nextボタンが押されたら、actクラスは次のliへ付け変わります。

myslide.js
  let click = true;
  const next = $('#next');
  const prev = $('#prev');
  let nextImg, prevImg, cloneImg;

  //次へ
  function nextSlide(){
    //連打クリック禁止
    if(click == true){
      click = false;
      //actの次のliを取得
      nextImg = $('.act').next();
      //クローンしないと元データが消えてしまった
      cloneImg = $(nextImg).children('img').clone();
      panelImg.append(cloneImg);
      //actの付替え
      $(nextImg).addClass('act').siblings().removeClass('act');
      //スライド
      panelImg.stop().animate({'margin-left': imgW * -1},600,'swing',
      function(){
        $('img:first-child',this).remove();
        $(this).css('margin-left', 0);
      //クリックできる状態へ
        click = true;
      });
    }
  }

  next.on('click',function(){
    nextSlide();
    //ナビの表示非表示
    navChange();
  });

・前へ
nextの逆です。
左側に画像を足すので、予めmarginをマイナス値に。

myslide.js
  //前へ
  function prevSlide(){
    //連打クリック禁止
    if(click == true){
      click = false;
      prevImg = $('.act').prev();
      cloneImg = $(prevImg).children('img').clone();
      //スライドブロックを画像一つ分マイナスに
      panelImg.prepend(cloneImg).css('margin-left',imgW * -1);
      $(prevImg).addClass('act').siblings().removeClass('act');
      //スライドはmargin-leftを0に
      panelImg.stop().animate({'margin-left': 0},600,'swing',
      function(){
        $('img:last-child',this).remove();
        click = true;
      });
    }
  }

  prev.on('click',function(){
    prevSlide();
    //ナビの表示非表示
    navChange();
  });

・ナビの表示制御
最初だったらprevを非表示、最後だったらnextを非表示に。
ナビが隠れることによってスライドは不可になります。

myslide.js
  navChange();
  //ナビの処理
  function navChange(){
    if($('#thumb li:last-child').hasClass('act')) {
      next.css('display','none');
      prev.css('display','block');
    } else if($('#thumb li:first-child').hasClass('act')) {
      prev.css('display','none');
      next.css('display','block');
    } else {
      prev.css('display','block');
      next.css('display','block');
    }
  }

・サムネイルクリック
画像指定以外、ほぼnextと同じです。

myslide.js
  //サムネイルクリック
  let point,imgNum;
  $('#thumb li').on('click',function(){
    //連打クリック禁止
    if(click == true){
      click = false;
      //クリックした場所を検索
      point = $('#thumb li').index(this);
      $(this).addClass('act').siblings().removeClass('act');
      imgNum = $('#thumb li').eq(point);
      //cloneではない方法で取ってみる
      panelImg.append('<img src="'+ $('img',imgNum).attr('src') +'" alt="">');
      panelImg.stop().animate({'margin-left': imgW * -1},600,'swing',
      function(){
        $('img:first-child',this).remove();
        $(this).css('margin-left', 0);
        click = true;
      });
      navChange();
    }
  });

・touchイベント
最後と最初の画像の場合、スワイプ制御したかったので、スワイプ不可の値として’stop’を代入しています。値は’left’と’right’以外は何でもいいです。

myslide.js
  //touchイベント
    let moveX, posiX;

    /* 指が触れたか検知 */
    $('.panel').on('touchstart', start_check);

    /* 指が動いたか検知 */
    $('.panel').on('touchmove', move_check);

    /* 指が離れたか検知 */
    $('.panel').on('touchend', end_check);

    //タッチ開始時の処理
    function start_check(e) {
      /* 現在の座標取得 */
      posiX = getX(e);
      /* 移動距離状態を初期化 */
      moveX = '';
    }

    //スワイプ中の処理
    function move_check(e) {
      if (posiX - getX(e) > 20) // 20px以上移動でスワイプと判断
          {
      /* 右→左と判断 */
          if($('#thumb li:last-child').hasClass('act')){
             //画像が最後だったら移動しない
             moveX = 'stop';
            } else {
             moveX = 'left';
            }
          } else if (posiX - getX(e) < -20)  // 20px以上移動でスワイプと判断
          {
          /* 左→右と判断 */
            if($('#thumb li:first-child').hasClass('act')){
              //画像が最初だったら移動しない
              moveX = 'stop';
              } else {
              moveX = 'right';
              }
          }
      }

      //指が離れた時の処理
    function end_check(e){
      if(moveX == 'left'){
        nextSlide(e);
        navChange();
      } else if(moveX == 'right'){
        prevSlide(e);
        navChange();
      }
    }

    function getX(e) {
      //横方向の座標を取得
      return e.originalEvent.touches[0].pageX;
    }

自動スライド

手動スライドにsetTimeoutをつけました。
エンドレスにループさせた為、navChange()は無くしてカルーセルにしています。
スワイプ不可の条件文も外しています。

myslide_timer.js
  function slideStart(){
      nextSlide();
      timerID = setTimeout(slideStart, 3000);
  }
  //タイマー起動
  slideStart();

横幅100%自動スライド

ファーストビューでよく見かけるブラウザ幅100%で端が見えているタイプです。
cssはかなり変更しました。

touch_timer_wide.html
<div class="panel">
    <figure class="panelImg">
        <img src="./image/slide_big6.jpg" alt="">
        <img src="./image/slide_big1.jpg" alt="">
        <img src="./image/slide_big2.jpg" alt="">
    </figure>
</div>

スライド領域のデフォルト画像は3つセットにし、真ん中がアクティブになります。なので次の画像指定は2つ先のものを取得します。
nextメソッドを入れたセレクタを変数に代入し、再度nextメソッドを使います。

myslide_timer_wide.js
function nextSlide(){
    //カルーセルにする為、最後のliになったら最初のliを指定
    if($('#thumb li:last-child').hasClass('act')){
      nextImg = $('#thumb li:first-child');
    } else {
      nextImg = $('.act').next();
    }

    if($('#thumb li:nth-last-child(2)').hasClass('act')){
      //画像クローン
      cloneImg = $('#thumb li:first-child').children('img').clone();
    } else {
    //画像クローンは二つ先を取得
      cloneImg = $(nextImg).next().children('img').clone();
    }

    panelImg.append(cloneImg);
    //actの付替え
    $(nextImg).addClass('act').siblings().removeClass('act');
    //スライド
    panelImg.stop().animate({'margin-left': imgW * -2},800,'swing',
    function(){
      $('img:first-child',this).remove();
      $(this).css('margin-left', imgW * -1);
      click = true;
    });
  }

モーダルスライド

モーダルウィンドウが浮かび上がってからattrメソッドで切り替わる仕様です。
サムネイルに画像の他、htmlデータも格納しました。

touch_modal.html
<!-- オーバーレイ -->
  <div id="overlay">
    <!-- 表示領域 -->
    <div id="slidePanel">
      <!-- サムネのデータより呼び出し -->
      <figure class="panelImg">
      <!-- attrメソッドで差し替え -->
        <img src="./image/slide1.jpg" alt="">
      </figure>
      <article id="modal_contents">
        <!-- htmlメソッドで丸ごと上書き -->
      </article>

      <div id="prev"><img src="./image/prev.svg" alt="前へ"></div>
      <div id="next"><img src="./image/next.svg" alt="次へ"></div>
      <div id="close_modal">×</div>
    </div>
    <!-- //.slidePanel -->
  </div>
  <!-- //#overlay -->

・サムネイルclick

myslide_modal.js
//サムネイルクリック
  let point,imgNum;
  $('#thumb li').on('click',function(){
    //連打クリック禁止
    if(click == true){
      click = false;
      point = $('#thumb li').index(this);
      //クリックした場所を検索
      $(this).addClass('act').siblings().removeClass('act');
      imgNum = $('#thumb li').eq(point);
      $('img',panelImg).attr('src',$('img',imgNum).attr('src'));
      $('#modal_contents').html($('.modal_text',imgNum).html());
      $('#overlay').stop().fadeIn(800,
      function(){
        click = true;
      });
      navChange();
    }
  });

  //閉じる
  $('#close_modal').on('click',function(){
    $('#overlay').stop().fadeOut(600);
  });

ソースはGithabにアップしています。class化はブランチしました。
https://github.com/mugikomugi/my_slide/tree/class_ver


まだまだ需要がありそうなスライドショー。
お得意様からオーソドックスなスタイルを要求されることもあったので
そんな時のためにストックとして保存しておくことにしました。
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?