jQuery でシンプルなループスライダーを作る

Category : jQuery

jQuery Simple Loop Slider

JavaScript というか jQuery のお勉強。
練習がてらコンテンツスライダーを作ってみました。
この手のものはチュートリアルもプラグインも山ほどありますが、必要な機能だけあって好きに使えるものが欲しかったので1から作りました。

プラグインをお求めの方は こちら をどうぞ。

欲しかった機能

  • シンプルに動くコンテンツスライダー。
  • ループ機能。最後の次は最初、最初の前は最後、という感じで動く。
  • 進む、戻るナビゲーション。
  • 自動でページネーションを設置。

これをやります。
デモとダウンロードは以下からどうぞ。

jQuery Simple Loop Slider DEMO

デモファイルをダウンロード

HTML と CSS

<div id="slider">

    <div class="slider-view">
        <div class="slider-container">
            <div><a href="#"><img src="images/image1.jpg" alt="" /></a></div>
            <div><a href="#"><img src="images/image2.jpg" alt="" /></a></div>
            <div><a href="#"><img src="images/image3.jpg" alt="" /></a></div>
            <div><a href="#"><img src="images/image4.jpg" alt="" /></a></div>
            <div><a href="#"><img src="images/image5.jpg" alt="" /></a></div>
        </div><!-- // .slider-container -->
    </div><!-- // .slider-view -->

    <a href="#" id="slide-prev">&laquo;</a><a href="#" id="slide-next">&raquo;</a>

</div>

ナビゲーションがなければ div は1個減らせます。
基本的には .slider-view を固定して .slider-container を横に動かす形となります。

#slider {
    position: relative;
    margin: 50px auto;
    border: 1px solid #aaa;
    border-radius: 10px;
    width: 482px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}

#slider a:focus {
    outline: 0;
}

.slider-view {
    position: relative;
    margin: 10px 40px 30px;
    border: 1px solid #bbb;
    width: 400px; /* スライダーで表示するエリアの大きさ */
    height: 160px;
    overflow: hidden; /* 必須 */
}

/* width は js で指定するのでここでは書かない */
.slider-container {
    position: absolute;
    top: 0;
    left: 0;
}

/* スライドするコンテンツ部分 */
.slider-container div {
    position: relative; /* ループ処理に使う */
    float: left;
    width: 400px; /* view と同じ大きさに */
    height: 160px;
}

/* デモ用 --------*/
.slider-container div a {
    display: block;
}

.slider-container div a:hover {
    color: #600;
}
/*-------- ここまで */

/* 進む、戻るの配置とデコレーション */
#slide-prev, #slide-next {
    position: absolute;
    top: 60px;
    color: #e0e0e0;
    font-size: 60px;
    line-height: 1;
    text-decoration: none;
    text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
}

#slide-prev {
    left: 2px;
}

#slide-next {
    right: 2px;
}

#slide-prev:hover,
#slide-next:hover {
    color: #ccc;
    text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
}

/* ページネーションの配置とデコレーション */
.slider-pagination {
    position: absolute;
    top: 177px;
    left: 50%;
    margin-left: -50px;
    width: 100px;
}

.slider-pagination a {
    float: left;
    margin: 5px 5px 0;
    border-radius: 5px;
    width: 10px;
    height: 10px;
    overflow: hidden;
    box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5) inset;
    background: #eee;
    text-indent: -9999px;
    vertical-align: middle;
}

.slider-pagination a:hover {
    background: #ccc;
}

/* 現在表示しているコンテンツと同じページナンバー */
.slider-pagination a.current {
    box-shadow: 1px 1px 2px rgba( 0,   0,   0, 0.5) inset,
                0   0   2px rgba(68, 170, 238, 0.5);
    background: #4ae;
}

position, width, height, overflow プロパティさえしっかり指定していればそんなに問題は起きないと思います。
特に .slider-viewoverflow: hidden; はないと悲惨なことになるので必ず指定します。

.slider-viewoverflow: hidden; を消した場合、ループ処理がどういう仕組になっているか分かりやすいと思いますので、試してみてもいいかもしれません。

jQuery

最初に各種設定をします。

var o = {
  speed   : 300, // スライドするスピード(ミリ秒)
  interval: 3000 // 次のスライドまでの時間(ミリ秒)
};

// 対象となる要素を変数に格納しておく
var $slider     = $('#slider'),
    $container  = $slider.find('div.slider-container'),
    $contents   = $container.children(),
    $firstChild = $contents.filter(':first-child'),
    $lastChild  = $contents.filter(':last-child');

// スライドが表示されるエリアのサイズ(スライドするコンテンツ1つ分と同じ)
var size = {
  width : $container.width(),
  height: $container.height() // 今回は使いません
};

// スライドするコンテンツの現在地の管理
var count = {
  min    : 0,
  max    : $contents.length,
  current: 0
};

// div.slider-container の width を設定する
// 前後にスライドコンテンツ1つ分のスペースを作る(ループ処理に使う)
$container.css({
  width      : size.width * ($contents.length + 2),
  marginLeft : -size.width,
  paddingLeft: size.width
});

次にスライドする動きと、共通処理を作ります。

var distance; // 移動距離を指定するのに使う
var slide = {

  // スライド(進む)
  next: function (index) {

          // 移動距離を出すための関数
          fnc.range(index, 'positive');

          // スライドアニメーション
          if(count.current < count.max - 1) {

            // 現在地が最後のコンテンツより前の場合の処理
            fnc.scroll(distance);

          } else {

            // 現在地が最後のコンテンツだった場合

            // 最初のコンテンツをコンテナの一番後ろまで移動
            $firstChild.css('left', size.width * $contents.length);

            // 一番最後のコンテンツの次のエリアにスライド
            $container.stop(true, false)
                      .animate({left: -distance}, o.speed,
                        // アニメーションコールバック関数
                        function () {
                          // 移動した最初のコンテンツを元の場所に戻す
                          $firstChild.css('left', 0);
                          // スライドしていったコンテナ自体も元の場所に戻す
                          $container.css('left', 0);
                        }
                      );

            // 現在地を -1 に (次の処理で 0 になる)
            count.current = -1;
          }

          // 現在地を 1 増やす
          fnc.counter(index, 'increment');

          // ページネーションのクラスを付け替える
          fnc.pageNav(count.current);
        },

  // スライド(戻る) / 基本的には next の逆の処理をするだけ
  prev: function (index) {
          fnc.range(index, 'negative');
          if(count.current > count.min) {
            fnc.scroll(distance);
          } else {
            $lastChild.css('left', -(size.width * $contents.length));
            $container.stop(true, false)
                      .animate({left: -distance}, o.speed,
                        function () {
                          $lastChild.css('left', '');
                          $container.css('left', -(size.width * ($contents.length - 1)));
                        }
                      );
            count.current = count.max;
          }
          fnc.counter(index, 'decrement');
          fnc.pageNav(count.current);
        }

};

// 共通で使われる関数
var fnc = {

  // 移動距離を出す
  range  : function (n, d) {
             if(n >= 0) {
               // ページネーションで指定するとき
               distance = size.width * n;
             } else {
               // それ以外 / 自動 or 進む、戻る
               var addNum;
               if(d === 'negative') addNum = -1; // Next
               if(d === 'positive') addNum = +1; // Prev
               distance = size.width * (count.current + addNum);
             }
           },

  // シンプルに移動距離分スライド
  scroll : function (d) {
             $container.stop(true, false).animate({left: -d}, o.speed);
           },

  // アニメーションするときに現在位置を増減する
  counter: function (n, c) {
             if(n >= 0) {
               // ページネーションで指定するとき
               count.current = n;
             } else {
               if(c === 'increment') count.current++; // 進む
               if(c === 'decrement') count.current--; // 戻る
             }
           },

  // ページネーションのクラス名を振りなおす
  pageNav: function (n) {
             $pagination.children('a').removeClass('current');
             $pagination.children('a:eq(' + n + ')').addClass('current');
           },

  // 進む、戻るをクリックしたときの処理
  pager  : function (d, e) {
               if(!$container.is(':animated')) {
                 clearInterval(start);
                 if(d === 'positive') slide.next(); // 進む
                 if(d === 'negative') slide.prev(); // 戻る
                 play();
               }
               e.preventDefault(); // リンククリック動作を無効にする
           }

};

説明が足りていないような気もしますが、たぶん分かるんじゃないかと思います。
ごめんなさい。

最後はイベント処理、自動スライド、ページネーションです。

var play, start;

// 自動スライド処理
// o.interval で指定した時間ごとに slide.next() を実行する
play = function () {
         start = setInterval(function () {
                     slide.next();
                 }, o.interval);
       };

// スライドコンテンツにホバーしたときの処理
$contents.hover(
  function () {
    // ホバーしたら自動スライドを停止
    clearInterval(start);
  },
  function () {
    // カーソルが離れたら再開
    play();
  }
);

// 進む、戻るの処理
// 共通関数部分を参照
$('#slide-prev').click(function (e) {
  fnc.pager('negative', e);
});

$('#slide-next').click(function (e) {
  fnc.pager('positive', e);
});

// ページネーションを入れる div.slider-pagination を作成
var $pagination = $('<div/>', {'class': 'slider-pagination'});

// スライドするコンテンツ数と同じ数の a 要素を作って div.slider-pagination に追加
$contents.each(function (i) {
  $('<a/>', {'href': '#'})
    .text(i + 1)
    .appendTo($pagination)

    // クリックイベントを一緒に追加しておく
    .click(function (e) {

      e.preventDefault(); // リンククリック動作を無効にする
      var indexNum = i; // クリックされたリンクのインデックス番号を取得
      clearInterval(start); // 自動スライドを停止

      // インデックス番号が現在地より大きい場合
      if(indexNum > count.current) {
        slide.next(indexNum);
      }
      // インデックス番号が現在地より小さい場合
      else if(indexNum < count.current) {
        slide.prev(indexNum);
      }
      play(); // 自動スライド再開
    });
});

// ページネーションを div#slider に追加
$pagination.appendTo($slider);

// ページネーションの最初に current というクラス名を付ける
$pagination.find('a:first-child').addClass('current');

これで処理は完了です。
最後に

play();

を書いて、自動スライドを開始して完成です。

デモに使っているものと説明とは順番が少し違っていますが、書いてあることは同じです。
最終的にできあがるものはデモページで使っている script.js をご覧ください。

このスクリプトを使いたい場合は、ソースを丸ごと転載とかしなければ好きに使ってもらって構いません。
もっと素敵なコードに改良されて使われるといいなと思っています。
あと、写真は自分で撮ったものです。こっちはダウンロードしたデモページ以外では使わないでください。

スライドの順番を管理するために、以下を参考にしました。

練習としてはなかなかのものになったんじゃないかなーと思いますが、もっとよくしていきたいですね。

ページネーションを作るときに一緒にイベントを設定したほうがいいとご指摘を受けたので修正しました。

Trackbacks & Pingbacks

Comments

aki

こんにちは。
質問なのですが
上記スライダーのpagenationに
スライダーごとのタイトルを表記することって可能でしょうか?

aki

まとりさま。
早速お返事いただきまして大変恐縮です。
ありがとうございます。

ご提示いただきましたサンプル拝見いたしました。
まさに私のやってみたいのはそれです!!
jQuery初心者なのですが、一から作るのは無理にしても
カスタマイズできるようになりたい、と思っていたところ
まとりさまのサイトのこのページを発見し
コードとにらめっこして試行錯誤しております。

ど、どうすれば、できるのでしょうか!
ご教示いただけますと幸いです。

ネコ、可愛いですね・・・。

まとり

@aki さん
上記のものは、この記事のスライダーをプラグインにした
http://unformedbuilding.com/articles/jquery-simple-loop-slider-js/
に少し手を加えたものです。ですので、
http://unformedbuilding.com/demo/test/jquery.simpleLoopSlider.js.custom/js/jquery.simpleloopslider.js
をそのまま使ってもらって構いません。
使い方は元々のプラグインと同じです。

$(function () {
  $('#myslider').simpleLoopSlider({
    controller: true,    // 前・次へ移動するボタンを表示するかどうか
    pagination: true,    // ページネーションを表示するかどうか
    autoSlide : true,    // 自動スタート、自動スライドをするかどうか
    interval  : 3000,    // 自動スライド時のスライド間隔(ミリ秒)
    duration  : 300,     // スライドアニメーションの早さ(ミリ秒)
    easing    : 'linear', // スライドアニメーションのイージング
    buttonText: ['タイトル1', 'タイトル2', 'タイトル3'] // スライドページ毎のタイトル
  });
});

こんな感じで、最後のオプションが追加されています。
タイトルは配列に入れてください。ただ、これは適当に追加したのでスライドページの数と合わない場合はバグるかもしれません。すみません。

元のものとはページネーションを作る部分が少し変更されています。どのへんが違うかは比べながらご確認ください。
私も初心者同然なので、そんなに難しいことはしていません。
改良点できそうなら改造して使っていただけると嬉しいです。

>ネコ、可愛いですね・・・。
ありがとうございますw

aki

まとりさま。

やったー!!できました!!
いや、もちろんプラグインを使用させていただくべきだってぇことは
重々わかっておりました。。。が、上記プラグイン化される前の
スクリプトでどうしてもスライダのタイトルを表示したく、ごちゃごちゃ
やってたら、ついに出来ました・・・(涙

あ、でもサイトの表示には作っていただいたプラグインを使いまっす。
絶対そっちのほうが便利ですもん。でも、できないのが悔しくて!

この度は、どうもありがとうございました~~感謝いたします。

まとり

@aki さん
それは何よりです。お疲れ様でした。
まずは自分でやってみようとするのはいいことだと思いますので、これからも頑張ってください。

けい

こちらを使わせて頂きました。

一つ疑問なのですがこちらはスライドにリンクをはることは出来ないのでしょうか?

リンクを画像にはったのですが、更新してもリンクが押せない状態になっていまして・・・

まとり

@けい さん
この記事内からダウンロードできるスクリプトには最後の方に

// for DEMO
$contents.find('a').click(function (e) {
  e.preventDefault();
});

という記述があるかと思います。
これはデモページのスライドでリンクを無効にするために書いています。
実際に使う場合には必要ありませんので消してしまっても結構です。

余計なお世話かもしれませんが、プラグイン化したものを使われたほうが楽かと思います。
やっていることはこの記事とほとんど同じです。

たつお

ひとつご質問があります。
xp+IE8の環境で確認すると、スライドする間画像ぶれているように見えます。(速度を落とすと)
解消する術はありますか?
ご教授のほど、よろしくお願いいたします。

まとり

@たつお さん
すみません、環境がないので確認できないです……。
それと、このコードはそんなにいいものではないので環境によっては重くなるとは思います。
もっと素晴らしい jQuery スライダーは本当にたくさんありますので、そういったもののご利用も検討してみてください。

ttr

こんにちは。こちら、とても役立っております。
それで、ひとつ、カスタマイズ方法を教えていただきたく。
最初のスライドのときは、前へ を非表示にし、
最後のスライドのときは、次へ を非表示。

これは、カンタンにできるでしょうか・・・?
まったくの初心者でして、ヒントか方法を教えていただきたいです。

ttr

まとりさんっ!これです!
多謝、多謝です!!
script.js参考に勉強します、本当にありがとうございます!

ttr

まとりさん。

昨日、教えていただいたスクリプトですが。
いろいろ組み込んでみましたが、
同一ページに複数のスライドがあり、id属性を重複できないということもあり。
で、うまくいきませんです。。

それで、以下を利用しようとしました。
http://unformedbuilding.com/articles/jquery-simple-loop-slider-js/

こちらのプラグインでの
.sls-prev、.sls-next の表示・非表示を設定する方法はございますか?

カスタマイズができるスキルではありませんので、
もしほかに使えそうもの等でもあれば、教えていただければ幸いです。

ttr

まとりさん

本当にありがとうございます!
丁寧な対応をしていただき、ありがとうございます。本当に感謝です。
いつもいろんな情報、本当に役立っています。
ありがとうございます!!!

たかこ

こんにちは

スライダーにリンクをつけたいので
デモに下記に変更しましたが
最初の画像が2番目になり最後の画像がだぶってでます。
初心者で詳しくわかりません。

スライダーで画像をクリックするとリンクするバナーを作りたい。
教えてください。よろしくお願いします。

<a href="…" rel="nofollow"></a>
<a href="…" rel="nofollow"></a>
<a href="…" rel="nofollow"></a>
<a href="…" rel="nofollow"></a>
<a href="…" rel="nofollow"></a>

<!– // .slider-container –>

<!– // .slider-view –>
<a href="#" rel="nofollow">&laquo;</a><a href="#" rel="nofollow">&raquo;</a>

まとり

@たかこ さん

すみませんが、おっしゃっている意味がよく分かりません。
そもそも、デモで使っている HTML はリンクが設定されていますので、それのリンク部分と画像のソースを書き換えて、スクリプトの最後の方にある

// for DEMO
$contents.find('a').click(function (e) {
  e.preventDefault();
});

を削除すればいいだけです。

あと、申し訳ありませんがコメント内ソースの URL は削除しました。

nakayama

大変参考になりました。
一からスライダーを作りたいと思っていたところ検索してこちらにたどり着きました。
スクリプトの記述が構造的にわかりやすくまとめられていたので、
解析もしやすかったです。
ありがとうございます!

Leave a Reply