【jQuery】プラグインなしの多機能スライダーをもう一度作成してみた【改訂版】

 

最近MacBookを買ったのですが、初めてのMacOSで操作に慣れない+膨大なインストール作業に辟易して早速ほこり被ってます。。 めちゃくちゃ暇なときにでも設定作業をしようと思いまス。

 

前回作成したスライドショーのコードをリファクタリングしてみる

 

アウトプットも兼ねて、今回はリファクタリングの仕方に重きを置いた記事内容を書いていこうと思います。

 

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。

wikipediaより引用

 

前回多機能スライドショーを実装しましたが、今回は前回と全く同じ挙動ではありますが、リファクタリングを施したのでコードの可読性がかなり上がっています

前回のクソコードと照らし合わせて見てみると面白いかもです。こんなに簡略化できるんだって思って僕はビックリです!

前回作成したスライドショーの記事

JQueryで多機能スライダーを一から実装してみた【プラグインなし】

 

 

綺麗なコードを書くうえで抑えておくべき要点

ベースとなる動きから順に実装していく(今回の場合は時間経過でスライドアニメーションを実行がベースとなる機能です。)

ベースの機能が完成したらそこに必要な機能を一つひとつ肉付けしていくとコードが取っ散らかりづらい

変数宣言は必要としている関数内に入れること。複数の関数に必要とされる変数はグローバルで宣言するようにする

jQueryのオブジェクトとしてDOM操作が伴う場合、変数に格納する 例:const $sliderWrap = $(“#sliderWrap”);

また、親のidを起点として子要素をfind()で取得することで負担が減ります

関数は一つひとつ切り分けて作成

インデックス番号など他の関数にも紐づけるような変数は使いまわす

全体のコード

小分けで一つずつ掘り下げて説明しますが、まず全体のコードを載せときます。

$(function () {

  function slider() {
    //変数宣言
    const $sliderWrap = $('#sliderWrap');
    const $slider = $sliderWrap.find('.slider');
    const $slides = $slider.find('.slides');
    const $slide = $slides.find('.slide');
    const $indicator = $sliderWrap.find('.indicator');
    const $prev = $sliderWrap.find('.prev');
    const $next = $sliderWrap.find('.next');
    const slideLength = $slide.length;
    let currentIndex = 0;
    let $indicatorItem = '';
    
    //インジケータの生成
    function addIndicator(){
      const indicatorHTML = '<li class="indicatorItem"></li>';
      for (let i = 0; i < slideLength; i++){
        $indicator.append(indicatorHTML);
      }
      $indicatorItem = $('.indicatorItem');
      $indicatorItem.eq(currentIndex).addClass('is-active');
    }
    
    //スライドクローン
    function cloneSlide() {
      const $lastSlide = $slides.find('li:last-child');
      const $firstSlide = $slides.find('li:first-child');
      $lastSlide.clone(true).prependTo($slides);
      $firstSlide.clone(true).appendTo($slides);
    }

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      $slides.animate({
        left: (currentIndex + 1) * -100 + '%'
      },duration);
      if(currentIndex == slideLength){
        currentIndex = 0;
        $slides.animate({
          left: (currentIndex + 1) * -100 + '%'
        },0);
      }else if(currentIndex == -1) {
        currentIndex = slideLength -1;
        $slides.animate({
          left: (currentIndex + 1) * -100 + '%'
        },0);
      }
      updateNav();
    }

    //スタートタイマー
    function startTimer() {
      const interval = 1000;
      timer = setInterval(nextSlide, interval);
    }

    //ストップタイマー
    function stopTimer() {
      clearInterval(timer);
    }

    //インジケータの更新
    function updateNav() {
      $indicatorItem.removeClass("is-active").eq(currentIndex).addClass("is-active");
    }

    //インジケータークリック処理
    function goSomeWhere() {
      if(!$slides.is(':animated')){
        currentIndex = $(this).index();
        changeSlide();
      }
    }
    //prevボタンの処理
    function prevSlide() {
      if(!$slides.is(':animated')){
        currentIndex--;
        changeSlide();
      }
    }
    //nextボタンの処理
    function nextSlide() {
      if(!$slides.is(':animated')){
        currentIndex++;
        changeSlide();
      }
    }

    //クリック、マウスイベント群
    function setEvent() {
      $slider.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });
      $prev.on('click', prevSlide);
      $next.on('click', nextSlide);
      $indicatorItem.on('click', goSomeWhere);
    }

    //初期発火イベント群
    function init() {
      addIndicator();
      cloneSlide();
      setEvent();
      startTimer();
    }
    //init発火
    init();
  }
  //slider発火
  slider();
});

根幹となるスライドアニメーションの関数の作成

作成していく順番としてまず最初は根幹となるスライドアニメーションの関数を作成していきます。

とりあえず単純に時間経過で右にスライドさせる動きを実装していきます。

$(function () {

  function slider() {

    //変数の設定
    const $sliderWrap = $('#sliderWrap');
    const $slider = $sliderWrap.find('.slider');
    const $slides = $slider.find('.slides');
    let currentIndex = 0;

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      currentIndex++;
      $slides.animate({
        left: (currentIndex) * -100 + "%"
      },duration);
    }
    //タイマースタート
    function startTimer() {
      const interval = 1000;
      timer = setInterval(changeSlide, interval);
    }
    startTimer();
  }
  slider();
});

 

変数の宣言について

1.起点となる親クラスを$('クラス名');で取得し、変数に格納させること

2.その親クラスからfindメソッドで子クラスを取得し、変数を格納させること

こうすることで、1回だけでDOMから要素を取得できるので、処理がより軽くなります。

 

挙動の確認

挙動をブラウザで確認すると、右にスライドする動きになっていると思います。ただ現状だとスライドの上限に達すると無限に右にスライドしてしまいます。

無限にスライドし続けるように改善していく

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      console.log(currentIndex);
      $slides.animate({
        left: (currentIndex) * -100 + "%"
      },duration);
      currentIndex++;
      if(currentIndex == slideLength){
        currentIndex = 0;
      }
    }

ifでスライドの上限に達した場合、currentIndexを0に初期化する処理を追加します。

そうすることでスライドが最初に戻り、無限スライドが出来上がります。

ですがこのままだと、最後のスライドから最初のスライドに切り替わるとき、動きがぎこちない感じなので次はそこをブラッシュアップしていきます。

現状の挙動↓↓

スライドの動きを滑らかにする

最後のスライドから最初のスライドに切り替わるときの挙動が、現状だとギュルルっと巻き戻るような感じですが、これをスムーズな挙動に直していきます。

こんな感じの挙動にしていきます↓↓

 

スライドをクローンする

前回スライドショーの記事にも書きましたが、一応こっちの記事でも説明加えておきます。

    //最初と最後のスライドをクローン
    function cloneSlide() {
      const $lastSlide = $slides.find("li:last-child");
      const $firstSlide = $slides.find("li:first-child");
      $lastSlide.clone(true).prependTo($slides);
      $firstSlide.clone(true).appendTo($slides);
    }

最初に表示させたいスライドと最後に表示させたいスライドをclone(複製)します。cloneしたスライドをappendTo(後),prependTo(前)で$(‘.slides’)に追加します。

cloneする理由

表示させたい画像を、前後に持ってくることで、滑らかな挙動の無限スライドを実現できます。

注意したいのが、クローン関数によってスライドのインデックスが表示させたいスライドと一個ずれます。これはクローンされた要素が追加されるためです。

簡単に図で表すとこんな感じです↓↓

 

 

よって、cloneSlide4のインデックスは0slide1のインデックスは1になります。なのでコードは以下のようになります。

if文の条件がtrueになったら、indexに0(初期位置)を代入して、表示するスライド位置をanimateメソッドで移動させます。移動にかかる時間は0を設定してください。

0とすることでアニメーションにかかる時間を一瞬で実行できます。これでスライドアニメーションが滑らかな挙動になります。

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      currentIndex++;
      $slides.animate({
        left: (currentIndex+1) * -100 + "%"
      },duration);
      
      if(currentIndex == slideLength){
        currentIndex = 0;
        $slides.animate({
          left: (currentIndex + 1) * -100 + '%'
        },0);
      }
    }

 

インジケーターの生成

インジケーターをJS側で生成していきます。JS側でHTML生成する理由として、スライド数に応じて可変で生成できる点です。HTMLをいちいち書き換える必要なくなるので楽になります。

    //インジケーターの生成
    function addIndicator(){
      indicatorHTML = "<li class=item></li>";
      for (let i = 0; i < slideLength; i++){
        $indicator.append(indicatorHTML);
      }
      $item = $(".item");
      $item.eq(currentIndex).addClass("is-active");
    }

      $item.eq(currentIndex).addClass("is-active");

このコードは初期位置となるインジケータにis-activeクラス(色変更)を付与する処理です。

$itemのインデックス番号とcurrentIndexの値が一緒の要素にaddClassを付与するといった処理で、この場合は一個目に色(白)がつきます↓↓

 

 

スライド移動に伴い、インジケーターもそれに応じて移動させる

    //スライドアニメーション
    function changeSlide() {
    ~~略~~
      updateNav(); //updateNav()関数の発火
    }

    //現在のスライド位置をインジケーターに表示
    function updateNav() {
      $item.removeClass("is-active");
      $item.eq(currentIndex).addClass("is-active");
    }

changeSlide()内のupdateNav()関数が発火するといった流れになります。

現在位置で色が変わる処理は単純にremoveメソッドとaddClassメソッドで付け替えしているだけです。

現状の挙動↓↓

 

 

タイマーのストップ機能

スライドショーにマウスを乗せるとタイマーがストップするようにしていきます。

    //タイマーの一時停止
    function stopTimer() {
      clearInterval(timer);
    }

    function setEvent() {
      $slides.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });
    }

setEventでイベント実行関数をまとめています。

・mouseenter マウスポインタが対象要素に被った時の処理を実行。

・mouseleave マウスポインタが対象要素から外れた時の処理を実行。

 

矢印ボタン押下で、その方向にスライドさせる

    //nextボタンの処理
    function nextSlide() {
      if(!$slides.is(":animated")){
        currentIndex++;
        changeSlide();
      }
    }

    function setEvent() {
      $slides.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });
      // $prev.on('click', prevSlide);
      $next.on('click', nextSlide);
      $indicatorItem.on('click', goSomeWhere);
    }

.isメソッドは対象の要素が含まれる場合、trueを返します。

で指定した要素とセレクタがマッチしないときにtrueを返す条件になります。

 

 

 

まとめ

だいぶ長くなりましたがこれで終わりです。リファクタリングって難しいです。

単純にコードが短ければいいってものでもないらしい。あくまで他人が見て理解しやすいかどうかが重要で、それが一番難しかったり。。

とりあえず良書と噂のリーダブルコードがほしい。

 

 

 

 

 

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です