jquery jQuery の fadeOut() を使ったスライドショー

2013年5月17日

プラグインを使わずに、jQuery を使って簡単なスライドショーを行う方法のメモ。

シンプルなスライドショー

そのページにアクセスすると自動的にスライドショーを行う単純なもの。

サンプル1

基本的な構造

  • 最初に表示する写真とそのタイトルを HTML に記述。
  • スライドショーで表示する写真のファイル名と情報をカンマ区切りで、非表示に指定した div 要素内に li 要素として記述しておく。
  • 表示する他の写真は、最初の写真と同じフォルダに配置

HTML

<div id="mainphoto">
<img src="images/photos/slideshow/P1030380.jpg" width="680" height="452" alt="photo title1">
<p id="title">photo title1</p>
</div><!-- end of #mainphoto -->

<div id="photolist">
<ul>
<li>P1030380.jpg, photo title1</li>
<li>P1030411.jpg, photo title2</li>
<li>P1030429.jpg, photo title3</li>
<li>P1030440.jpg, photo title4</li>
<li>P1030447.jpg, photo title5</li>
<li>P1030459.jpg, photo title6</li>
<li>P1030600.jpg, photo title7</li>
<li>P1030630.jpg, photo title8</li>
<li>P1030664.jpg, photo title9</li>
<li>P1030694.jpg, photo title10</li>
<li>P1030819.jpg, photo title11</li>
<li>P1030858.jpg, photo title12</li>
</ul>
</div><!-- end of #photolist -->

img 要素は絶対配置(position:absolute)を指定。

CSS

div#mainphoto {
  float: left;
  width: 680px;
  height: 452px;
  padding: 0;
  margin: 0;
  position: relative;
  
}
/*---img 要素---*/
div#mainphoto img {
  position: absolute;
  top: 0;
  left: 0;
}
/*---写真のタイトルを表示する p 要素---*/
div#mainphoto p#title {
  position: absolute;
  z-index: 10;
  bottom: 10px;
  right: 30px;
  font-size: 11px; font-size: 1.1rem;
  color: #FFF;  
}
 
/*---表示する写真の情報を記述しておく div 要素---*/
div#photolist {
  display: none;
}

jQuery の記述

  • 次に表示する写真のインデックス(nextphoto)を初期化(最初は0で表示されているので次の1に指定)
  • 写真を表示する関数(showphoto)を作成。
  • 写真の情報を記述した li 要素のラップ集合を「photolist$」に格納
  • 写真の合計数( li 要素の数)を「photocount」に取得
  • div 要素(div#photolist)内に記述した li 要素の「nextphoto」に対応するテキストを「info」に取得
  • ファイル名とタイトルをそれぞれ余分な空白を取り除き「src」と「alt」に格納
  • 次に表示する画像要素の HTML を作成し「nextimg」に格納
  • 現在表示中の写真のタイトルを fadeOut で非表示にする。
  • 現在表示中の img 要素の前に before() を使って、次に表示する画像要素を挿入する。
  • 現在表示中の img 要素(img:last)を fadeOut で徐々に非表示にしていくと、挿入された次の画像が表示されていく。
  • 非表示になった img 要素は不要なので削除する。
  • 写真のタイトルを fadeIn で表示する。
  • 更に次の写真を表示するために nextphoto をインクリメントする。
  • nextphoto が写真の総数に達したら「0」にリセット。
  • 上記を window.setInterval() を使って繰り返す。
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  var nextphoto = 1;
  function showphoto() {
    var photolist$ = $('div#photolist li');
    var photocount = photolist$.length;
    var info = $(photolist$.get(nextphoto)).text().split(',');
    var src = $.trim(info[0]);
    var alt = $.trim(info[1]);
    var nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    $('div#mainphoto p#title').fadeOut(300);
    $('div#mainphoto img').before(nextimg);
    $('div#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
      $('div#mainphoto p#title').text(alt).fadeIn(200);
    });
    nextphoto ++;
    if(nextphoto == photocount ) nextphoto = 0;
  }
  
  var timer = window.setInterval(showphoto, 3000);
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
  
});
</script>

fadeOut() の代わりに animate() を使う

クロスフェードの代わりに animate() を使ってスライドさせて表示させる。

サンプル1-2

HTML は同じ。

CSS ではメインの画像の領域に「overflow: hidden;」を追加。その他は同じ。

CSS

div#mainphoto {
  float: left;
  width: 680px;
  height: 452px;
  padding: 0;
  margin: 0;
  position: relative;
  overflow: hidden;
}

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  var nextphoto = 1;
  function showphoto() {
    var photolist$ = $('div#photolist li');
    var photocount = photolist$.length;
    var info = $(photolist$.get(nextphoto)).text().split(',');
    var src = $.trim(info[0]);
    var alt = $.trim(info[1]);
    var nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    $('div#mainphoto p#title').fadeOut(300);    
    $('div#mainphoto img').before(nextimg);
    //この部分を変更
    $('div#mainphoto img:last').stop().animate({
      marginLeft: '-680px'
      }, 600, 
      function(){
        $(this).remove();
        $('div#mainphoto p#title').text(alt).fadeIn(200);
    });
    
    nextphoto ++;
    if(nextphoto == photocount ) nextphoto = 0;
  }
  
  var timer = window.setInterval(showphoto, 3000);
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
  
});
</script>

サンプル1-2では、元の画像がスライドして、その下に新しい画像が表示されるが、以下は新しい画像がスライドして上に表示される例。

サンプル1-3

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  var nextphoto = 1;
  function showphoto() {
    var photolist$ = $('div#photolist li');
    var photocount = photolist$.length;
    var info = $(photolist$.get(nextphoto)).text().split(',');
    var src = $.trim(info[0]);
    var alt = $.trim(info[1]);
    var nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    $('div#mainphoto p#title').fadeOut(300);
    //この部分を変更  
    var new_img$ = $(nextimg).css('left', '680px');
    new_img$.insertAfter('#mainphoto img').animate({
      left: 0
      }, 600, function() {
      $('#mainphoto img:first').remove();
      $('div#mainphoto p#title').text(alt).fadeIn(200);
    });
    
    nextphoto ++;
    if(nextphoto == photocount ) nextphoto = 0;
  }
  
  var timer = window.setInterval(showphoto, 3000);
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
  
});
</script>

カルーセルのように表示する

カルーセルパネルの作り方を参考に、2枚の写真を一緒にスライドさせてみる。

サンプル1-4

今までのサンプルとは構造が異なるので HTML、CSS ともに新たに作成。

HTML

<div id="slider_wrap">
    <div id="slider">
        <div id="slider_inner">
            <ul id="photo_list">
                <li><a href="#"><img src="images/1.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/2.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/3.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/4.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/5.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/6.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/7.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/8.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/9.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/10.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/11.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/12.jpg" alt=""></a></li>
            </ul>
        </div><!-- end of #slider_inner --> 
    </div><!-- end of #slider --> 
</div><!-- end of #slider_wrap -->

CSS

#slider_wrap {
  margin: 20px auto;
  width: 720px; 
  height: 423px; 
  padding: 20px;
  background: #EFEFEF;
  position: relative;
  border: 1px solid #CCC;
}

#slider {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#photo_list {
  /*width: 640px;  幅は指定しない*/
  height: 383px; 
  padding: 20px;
  list-style-type: none;
  float: left;
  background: #999;
}
#photo_list li {
  float: left;
  margin-right: 20px; 
  display: inline;
}
#photo_list img {
  border: none;
}

jQuery では、setInterval を使って「slider_inner」を画像1つ分左にずらし、先頭の画像を最後に付け替える。

関連ページ:「カルーセルパネル(スライドパネル)の表示

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  
  var photo_list$ = $('#photo_list');
  var li$ = $('#photo_list li');
  //写真の数
  var li_count = li$.length;
  //li 要素の幅 + その左右マージン
  var li_width = li$.width() + parseInt(li$.css('margin-left'), 10) + parseInt(li$.css('margin-right'), 10);
  //ul 要素の左右パディング
  var ul_padding = parseInt(photo_list$.css('padding-left') , 10) + parseInt(photo_list$.css('padding-right') , 10);
  var slider_inner$ = $('#slider_inner');
  //slider_innerの幅を設定(li 要素の幅 * その個数 + パディング)
  slider_inner$.css('width', (li_width * li_count + ul_padding) + 'px');
  //最後の li 要素を最初の位置へ
  $('#photo_list li:last').prependTo(photo_list$);
  //最後の li 要素を最初の位置へ移動した分ずらす
  slider_inner$.css('margin-left', '-' + li_width + 'px');  
  
  var timer = setInterval(function(){
    slider_inner$.stop().animate({
      marginLeft: parseInt(slider_inner$.css('margin-left'), 10) - li_width + 'px'
    }, 800,
    function(){
      slider_inner$.css('margin-left', '-' + li_width + 'px');
      $('#photo_list li:first').appendTo(photo_list$);
    });
  },2500);
  
});
</script>

クッキーを使いこのページに戻った場合は前回の写真から表示する場合

写真がある程度の数になった場合、毎回最初の写真から表示しなおすのも何なので、このページに戻った場合、前回表示した写真から表示するようにする方法。

サンプル2

  • クッキーは jQuery Cookie Plugin を利用して使用。
  • 前回表示した写真の情報(インデックス)はクッキーに保存する。
  • 以下の例は、セッションクッキーを使ったものでブラウザを閉じるとクッキーは削除され、次回は最初の写真から表示される。
  • クッキーに有効期限を指定することも可能。但し、写真の枚数を変更した場合などは、クッキーの名前を変更する必要が生じる。
  • クッキーが登録されていなければ、表示する写真のインデックスをクッキーに登録して保存する。
  • この時、初回アクセス時は最初の写真を表示するため、登録するインデックスは「0」とする。
  • クッキーが使えない環境の場合も考慮する必要があるので、そのような場合も「0」にする。
  • 最初に表示させる写真とそのタイトルをクッキーの値を使って変更。
  • 関数の中では最初に写真のインデックスをインクリメントさせる。
  • 後は前述の方法とほぼ同じ。ただ、毎回関数を実行するたびに表示する写真のインデックスをクッキーに登録する。
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="jquery.cookie.js"></script>
<script>
jQuery(function($){
  if(!$.cookie('nextimage')) {
    //クッキーが設定されていなければクッキー'nextimage'を設定
    $.cookie('nextimage', 0, { path: '/' } );  
  }

  //変数 nextphoto(写真のインデックス) にクッキーの値を格納。
  //クッキーが使えない場合(undefined)は「0」を代入。
  var nextphoto = $.cookie('nextimage') ? $.cookie('nextimage') : 0;
  
  //写真の情報を記述した li 要素のラップ集合
  var photolist$ = $('div#photolist li');
  //写真の合計数( li 要素の数)
  var photocount = photolist$.length;
  
  var p_title$ = $('div#mainphoto p#title');
  //div#photolist 内に記述した li 要素の「nextphoto」に対応するテキストを「info」に取得
  var info = $(photolist$.get(nextphoto)).text().split(',');
  var src = 'images/photos/slideshow/' + $.trim(info[0]);
  var alt = $.trim(info[1]);
  //画像にパス(src)を指定
  $('div#mainphoto img').attr('src', src);
  //タイトルに alt を指定
  p_title$.text(alt);
    
  function showphoto() {
    nextphoto ++;
    if(nextphoto == photocount ) nextphoto = 0;
    $.cookie('nextimage', nextphoto, { path: '/' } );    //写真のインデックスをクッキーに登録
    
    info = $(photolist$.get(nextphoto)).text().split(',');
    src = $.trim(info[0]);
    alt = $.trim(info[1]);
    console.log($.cookie('nextimage'));
    var nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    p_title$.fadeOut(300);
    $('div#mainphoto img').before(nextimg);
    $('div#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
      p_title$.text(alt).fadeIn(200);
    });  
  }
  
  var timer = window.setInterval(showphoto, 3000);
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
});
</script>

コントロールの追加

「停止」ボタンや現在表示されている写真を示す画像を追加。

サンプル3

  • 「停止」ボタンをクリックするとスライドショーを停止
  • 写真に対応する丸い画像を表示し、現在表示されているものは色を変える
  • 写真に対応する丸い画像をクリックするとスライドショーを停止してそれに対応する写真を表示
  • その際に「停止」ボタンの表示を変更する

写真の数を表す画像は HTML に1つだけ記述しておき、jQuery で写真の数分追加する。

HTML

<div id="mainphoto">
<img src="images/photos/slideshow/P1030380.jpg" width="680" height="452" alt="photo title1">
<p id="title">photo title1</p>
</div><!-- end of #mainphoto -->

<!-- 写真の数を表す画像 -->
<div id="icon"><a href="#"><img src="images/slider-icon.png" height="14" width="14"></a></div>

<p id="stop">Stop</p><!-- 停止ボタン -->

<div id="photolist"><!-- 写真情報のリスト(非表示) -->
<ul>
<li>P1030380.jpg, photo title1</li>
<li>P1030411.jpg, photo title2</li>
<li>P1030429.jpg, photo title3</li>
<li>P1030440.jpg, photo title4</li>
<li>P1030447.jpg, photo title5</li>
<li>P1030459.jpg, photo title6</li>
<li>P1030600.jpg, photo title7</li>
<li>P1030630.jpg, photo title8</li>
<li>P1030664.jpg, photo title9</li>
<li>P1030694.jpg, photo title10</li>
<li>P1030819.jpg, photo title11</li>
<li>P1030858.jpg, photo title12</li>
</ul>
</div><!-- end of #photolist -->

CSS

div#mainphoto {
  width: 680px;
  height: 470px;
  padding: 0;
  margin: 0 auto;
  position: relative;  
}
/*---img 要素---*/
div#mainphoto img {
  position: absolute;
  top: 0;
  left: 0;
}
/*---写真のタイトルを表示する p 要素---*/
div#mainphoto p#title {
  position: absolute;
  z-index: 10;
  bottom: -10px;
  right: 0;
  font-size: 14px;
  color: #EEE;  
}
 
/*---表示する写真の情報を記述しておく div 要素---*/
div#photolist {
  display: none;
}

#stop {
  cursor: pointer;
  background: #666;
  color: #FFF;
  border: 1px solid #ccc;
  width: 50px;
  height: 2em;
  text-align: center;
  line-height: 2em;
  margin: -30px 0 20px 60px;
}

#stop.start {
  background: #eee;
  color: #666;
  border-color: #666;
}

#icon {
  padding-top: 10px;
  width: 30px;
  margin: 0 auto;
}

#icon a {
  padding: 8px;  
}

写真に対応する画像をクリックした際の処理では、すでに「停止」ボタンが押されていなければスライドショーを停止する。

また、スライドショーを停止した場合は「停止」ボタンに「start」というクラスを追加して、表示を変更する。

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  
  var photolist$ = $('#photolist');
  var photolist_li$ =  $('#photolist li');
  var photo_count = photolist_li$.length;
  
  //現在表示されている写真のインデックス(初期化)  
  var current_photo = 0;
  var is_first = true;
  function showphoto() {
    //初回のみインデックスを増加(同じ写真がすでに表示されているため)
    if(is_first) {
      current_photo ++;
      is_first = false;
    }
    var info = $(photolist_li$.get(current_photo)).text().split(',');
    var src = $.trim(info[0]);
    var alt = $.trim(info[1]);
    var nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    $('div#mainphoto p#title').fadeOut(100);
    $('div#mainphoto img').before(nextimg);
    $('div#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
      $('div#mainphoto p#title').text(alt).fadeIn(100);
    });
    setup();
    current_photo ++;
    if(current_photo == photo_count ) current_photo = 0;
  }
    
  var icon_width = $('#icon').width();
  //写真の数を表す画像は HTML に1つだけ記述してあるのでパネルの数分生成
  if(photo_count > 1) {
    for(var i = 0; i < photo_count -1 ; i++) {
      $('#icon a:first').clone().appendTo('#icon');
    }
    $('#icon').css('width', icon_width * photo_count);    
  }
  var icon_imgs$ = $('#icon img');
  var icon_links$ = $('#icon a');
  //現在表示されている写真に対応する画像の背景を指定
  $(icon_imgs$.get(current_photo)).attr('src', "images/slider-icon-selected.png");
  
  var photo_img$ = $('#photo_list img');
  //写真に対応する画像の title 属性にリストの情報を設定
  icon_imgs$.each(function(index) {
    var info = $(photolist_li$.get(index)).text().split(',');
    var alt = $.trim(info&#91;1&#93;);
        $(this).attr('title', alt);
    });  
  
  //写真に対応する画像のソースを指定(現在表示されているものは別の画像を設定)
  function setup() {
    icon_imgs$.attr('src', "images/slider-icon.png");
    $(icon_imgs$.get(current_photo)).attr('src', "images/slider-icon-selected.png");    
  }
  
  //写真に対応する画像をクリックした際の処理
  icon_links$.click(function() {
    if(!is_stopped) {
      stop_slide();
    }
    current_photo = icon_links$.index($(this));
    setup();
    showphoto();    
  });
  
  //「停止」ボタンをクリックした際の処理
  var stop$ = $('#stop');
  stop$.click(function() {
    if(is_stopped) {
      start_slide();
      stop$.removeClass('start');
    }else{
      stop_slide();
      stop$.addClass('start');      
    }  
  });
  
  var timer;
  var is_stopped = false;
  //スライドショーを開始する関数
  function start_slide() {
    timer = window.setInterval(showphoto, 3000);
    stop$.text('Stop');
    stop$.removeClass('start');
    is_stopped = false;
  }
  
  //スライドショーを停止する関数
  function stop_slide() {
    window.clearInterval(timer);
    stop$.text('Start');
    stop$.addClass('start');
    is_stopped = true;
  }
  
  //スライドショーを開始
  start_slide();
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
});
</script>

「進む」「戻る」ボタンの追加

サンプル4

「進む」「戻る」ボタンを追加してみる。構造は「サンプル1-4」を元に作成。

HTML

<div id="slider_wrap">
    <!-- 「戻る」ボタン -->
    <p id="slider_prev"><img src="images/slider-prev.png" height="30" width="20" alt="previous"></p>
    <!-- 「進む」ボタン -->
    <p id="slider_next"><img src="images/slider-next.png" height="30" width="20" alt="next"></p>
    <div id="slider">
        <div id="slider_inner">
            <ul id="photo_list">
                <li><a href="#"><img src="images/1.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/2.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/3.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/4.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/5.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/6.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/7.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/8.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/9.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/10.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/11.jpg" alt=""></a></li>
                <li><a href="#"><img src="images/12.jpg" alt=""></a></li>
            </ul>
        </div><!-- end of #slider_inner --> 
    </div><!-- end of #slider --> 
    <div id="icon"><a href="#"><img src="images/slider-icon.png" height="14" width="14"></a></div>
    <div id="title"><p>title</p></div>
    <p id="stop">Stop</p>
</div><!-- end of #slider_wrap -->

CSS

#slider_wrap {
  margin: 20px auto;
  width: 720px; 
  height: 423px; 
  padding: 20px 20px 80px 20px;
  background: #EFEFEF;
  position: relative;
  border: 1px solid #CCC;
}
#slider_prev {
  position: absolute;
  top: 205px; 
  left: 10px;
  cursor: pointer;
}
#slider_next {
  position: absolute;
  top: 205px; 
  right: 10px;
  cursor: pointer;
}
#slider {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#photo_list {
  /* 幅は指定しない */
  height: 383px; 
  padding: 20px;
  list-style-type: none;
  float: left;
  background: #999;
}
#photo_list li {
  float: left;
  margin-right: 20px; 
  display: inline;
}
#photo_list img {
  border: none;
}
#stop {
  cursor: pointer;
  background: #666;
  color: #FFF;
  border: 1px solid #999;
  width: 50px;
  height: 2em;
  text-align: center;
  line-height: 2em;
  margin: -40px 0 0 0;
}

#stop.start {
  background: #ccc;
  color: #666;
  border-color: #aaa;
}

#icon {
  padding-top: 10px;
  width: 30px;
  margin: 0 auto;
}

#icon a {
  padding: 8px;
  
}

#title {
  padding-top: 15px;
  width: 200px;
  margin: 0 auto;
}
#title p {
  text-align: center;
  font-size:14px;
  color: #999;
}

jQuery では、写真のスライドアニメーションの途中で「進む」「戻る」ボタンをクリックできないように非表示にし、写真の数を表す画像をクリックしても何もしない「return false」ようにする(アニメーションの途中でクリックされると、位置がずれてしまうため)。

また写真の数を表す画像をクリックした場合はどちらの方向に移動するかは、近いほうを選択するようにしてみた。

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  var photo_list$ = $('#photo_list');
  var li$ = $('#photo_list li');
  var li_count = li$.length;  //写真の数
    
  //現在表示されている画像のインデックス(初期化)
  var current_image = 0;  
  var icon_width = $('#icon').width();
  //写真の数を表す画像は HTML に1つだけ記述してあるのでパネルの数分生成
  if(li_count > 1) {
    for(var i = 0; i < li_count -1 ; i++) {
      $('#icon a:first').clone().appendTo('#icon');
    }
    $('#icon').css('width', icon_width * li_count);    
  }
  var icon_imgs = $('#icon img');
  var icon_links = $('#icon a');
  //現在表示されている写真に対応する画像の背景を指定
  $(icon_imgs.get(current_image)).attr('src', "images/slider-icon-selected.png");
  
  var photo_img$ = $('#photo_list img');
  icon_imgs.each(function(index) {
        $(this).attr('title', $(photo_img$.get(index)).attr('alt'));
    });  
      
  //li 要素の幅 + その左右マージン
  var li_width = li$.width() + parseInt(li$.css('margin-left'), 10) + parseInt(li$.css('margin-right'), 10);
  //ul 要素の左右パディング
  var ul_padding = parseInt(photo_list$.css('padding-left') , 10) + parseInt(photo_list$.css('padding-right') , 10);
  var slider_inner$ = $('#slider_inner');
  //slider_innerの幅を設定(li 要素の幅 * その個数 + パディング)
  slider_inner$.css('width', (li_width * li_count + ul_padding) + 'px');
  //最後の li 要素を最初の位置へ
  $('#photo_list li:last').prependTo(photo_list$);
  //最後の li 要素を最初の位置へ移動した分ずらす
  slider_inner$.css('margin-left', '-' + li_width + 'px');  
  
  var prev_next$ = $('#slider_prev, #slider_next');
  var default_speed = 700;
  
  //クリックイベントで使用する関数(画像をスライドさせるアニメーション)
  //アニメーション中かどうかのフラグ
  var is_animate = false;
  function animate_image(margin_left, option, speed, delta) {
    is_animate = true;
    if(speed) { var speed = speed; }else{ var speed = default_speed; }
    //「進む」「戻る」ボタンを非表示に(クリックされると位置がずれる可能性があるため)
    prev_next$.hide();
    slider_inner$.stop().animate({
      marginLeft: margin_left + 'px'
    }, speed,
    function(){
      //「進む」「戻る」ボタンを表示
      prev_next$.show();      
      switch(option) {
        case 1:  
          //$('#slider_prev').click()の際の処理
          $('#photo_list li:last').prependTo(photo_list$);          
          break;
        case 2:
            //$('#slider_next').click()の際の処理
          $('#photo_list li:first').appendTo(photo_list$);        
          break;
        case 3:  
          //icon_links.click(  )の際の処理(特になし)
          break;
        case 4: 
          //icon_links.click(  )の際の処理
          //移動した分の写真をリストの最後に追加
          for(var i = 0; i < delta; i ++) {
            $('#photo_list li:first').appendTo(photo_list$);
          }
          break;
        default:
          break;      
      }
      //全てに共通の処理
      slider_inner$.css('margin-left', '-' + li_width + 'px');
      is_animate = false;
    });
  }
  //現在表示されているパネルに対応する画像を変更とボタンを表示する処理
  function setup() {
    icon_imgs.attr('src', "images/slider-icon.png");
    $(icon_imgs.get(current_image)).attr('src', "images/slider-icon-selected.png");    
  }
  
  function show_title() {    
    $('#title p').text($(photo_img$.get(current_image)).attr('alt'));
  }
  show_title();
  
  $('#slider_prev').click(function(){  
    if(!is_stopped) {
      stop_carousel();
    }
    animate_image(parseInt(slider_inner$.css('margin-left'), 10) + li_width, 1);
    if(current_image > 0 ) {
      current_image --;
    }else{
      current_image = li_count -1;
    }
    setup();
    show_title() ;
  });
  
  $('#slider_next').click(function(){  
    if(!is_stopped) {
      stop_carousel();
    }  
    animate_image(parseInt(slider_inner$.css('margin-left'), 10) - li_width, 2);
    current_image = (current_image + 1) % li_count;
    setup();
    show_title();
  });
  
  icon_links.click(function(){
    //画像をスライドさせるアニメーション中の場合は何もしない(位置がずれるため)
    if(is_animate) return false;
    //スライドショーが停止されていない場合は停止する
    if(!is_stopped) {
      stop_carousel();
    }
    //クリックされた画像の title 属性を タイトルに
    $('#title p').text($(this).find('img').attr('title'));
    var this$ = $(this);
    //クリックされた画像のインデックスを取得
    var icon_index = icon_links.index(this$);
    //現在の写真を示す画像のインデックスを取得
    var current_icon_index;
    icon_imgs.each(function(n) {
            if($(this).attr('src') == "images/slider-icon-selected.png") {
        current_icon_index = n;
      }
        });
    //インデックスの差分を取得
    var delta = Math.abs(icon_index - current_icon_index);
    //逆方向への移動距離を算出するための値
    var alpha = li_count - delta;
    //それぞれのインデックスを比較して処理を実行
    //クリックされた画像のインデックスが現在選択状態の画像のインデックスより小さい場合
    if(icon_index < current_icon_index){
      //差分が写真の数の半分より大きい場合(どちら側へ移動するかを決めるため)
      if(delta > li_count / 2) {
        var margin_left =  parseInt($('#slider_inner').css('margin-left'), 10) - li_width * alpha;  
        animate_image( margin_left, 4, 1500, alpha);      
        current_image -= delta;
        setup();
      }else{
        //移動する前に移動分の画像の位置を変更しておく
        for(var i = 0; i < delta; i ++) {
          $('#photo_list li:last').prependTo(photo_list$);
        }  
        slider_inner$.css('margin-left', '-' + li_width * (delta +1) + 'px');
        var margin_left = parseInt($('#slider_inner').css('margin-left'), 10) + li_width * delta;
        animate_image( margin_left, 3, 800, delta);
        current_image -= delta;
        setup();
      }  
    }else{  
      if(delta > li_count / 2) {
        //移動する前に移動分の画像の位置を変更しておく
        for(var i = 0; i < alpha; i ++) {
          $('#photo_list li:last').prependTo(photo_list$);
        }  
        slider_inner$.css('margin-left', '-' + li_width * (alpha +1) + 'px');
        var margin_left = parseInt($('#slider_inner').css('margin-left'), 10) + li_width * alpha;
        animate_image( margin_left, 3, 1500, alpha);
        current_image += delta;
        setup();
      }else{
        var margin_left =  parseInt($('#slider_inner').css('margin-left'), 10) - li_width * delta;  
        animate_image( margin_left, 4, 800, delta);      
        current_image += delta;
        setup();
      }
    }  
    return false;    
  });
  
  function carousel() {
    animate_image(parseInt(slider_inner$.css('margin-left'), 10) - li_width, 2);
    current_image = (current_image + 1) % li_count;
    setup();
    show_title();
  }
    
  var timer;
  var is_stopped = false;
  var stop$ = $('#stop');
  function start_carousel() {
    timer = setInterval(function(){
      carousel();
    },2500);
    stop$.text('Stop');
    is_stopped = false;
    stop$.removeClass('start');
  }
  
  function stop_carousel() {
    clearInterval(timer);
    stop$.text('Start');
    is_stopped = true;
    stop$.addClass('start');
  }
  
  stop$.click(function(){
    if(is_stopped) {
      start_carousel();      
    }else{
      stop_carousel();      
    }    
  });
  start_carousel();
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
});
</script>

その他

スライドショーを自動で開始して、サムネイルのパネルもそれに連動してスライドするものを作成しようとしたが、時々ブラウザがハングアップする現象が発生する。

setInterval の代わりに setTimeout を使って再帰的に呼び出すようにしてもハングアップするので、原因は何か他にあると思うが、今は保留。

このサンプルはまだ不完全なものなので、ブラウザがハングアップする可能性があるので注意してください。(問題が発生しても保障できません。)あくまでも個人的な練習のようなもので将来何かいい方法が思いつけば作り直すためのものです(覚えていれば)。

サンプル5(注意!)ブラウザがハングアップする可能性があります。

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
jQuery(function($){
  
  //パネルの総数
  var panel_count = $('#slider_inner ul.panel').length;
  var slider_width = $('#slider_wrap').width();
  //パネル全体を格納する div 要素の幅を「表示領域の横幅 X パネルの数」に設定
  var slider_inner$ = $('#slider_inner');
  slider_inner$.css('width', slider_width * panel_count + 'px');
  //現在表示されているパネルのインデックス(初期化)
  var current_panel = 0;
  //どのパネルが表示されているかを示す画像を表示する領域の幅
  var icon_width = $('#icon').width();
  //どのパネルが表示されているかを示す画像要素のパス(src)
  var icon_path = $('#icon a:first img').attr('src');
  //現在表示されているパネル示す画像要素のパス
  var icon_selected_path = 'images/slider-icon-selected.png';
  //パネルの数を表す画像は HTML に1つだけ記述してあるのでパネルの数分追加生成
  if(panel_count > 1) {
    for(var i = 0; i < panel_count -1 ; i++) {
      $('#icon a:first').clone().appendTo('#icon');
    }
    //「panel_count」を使ってパネルの数を表す画像の領域の幅を設定
    $('#icon').css('width', icon_width * panel_count);    
  }
  //パネルの数を表す画像のラップ集合を「icon_imgs」に格納
  var icon_imgs = $('#icon img');
  //パネルの数を表す画像のリンクのラップ集合を「icon_links」に格納
  var icon_links = $('#icon a');
  //現在表示されているパネルに対応する画像を変更(異なる色にする)
  $(icon_imgs.get(current_panel)).attr('src', icon_selected_path);
  //「進む」「戻る」ボタン
  var prev_next$ = $('#slider_prev, #slider_next');  
  //スライドショーが停止しているかどうか
  var stop_slide = true;
      
  icon_links.click(function(){
    //パネルをスライドさせるアニメーション中の場合は何もしない(位置がずれるため)
    if(is_animate) return false;
    //スライドショーを停止
    stop_slideshow();
    stop_slide = true;
    var this$ = $(this);
    //クリックされた画像のインデックスを取得
    var icon_index = icon_links.index(this$);
    //現在のパネルを示す画像のインデックスを取得
    var current_icon_index;
    icon_imgs.each(function(n) {
            if($(this).attr('src') ==  icon_selected_path) {
        current_icon_index = n;
      }
        });
    //現在のパネルを示す画像のインデックスを更新
    current_panel = icon_index;
    icon_setup();  
    //インデックスの差分を取得
    var delta = Math.abs(icon_index - current_icon_index);
    //それぞれのインデックスを比較して処理を実行
    if(icon_index < current_icon_index){
      animate_panel(parseInt($('#slider_inner').css('margin-left'), 10) + slider_width * delta);
    }else{    
      animate_panel(parseInt($('#slider_inner').css('margin-left'), 10) - slider_width * delta);
    }
    return false;    
  });
  
  var photo_per_panel = $('ul#first li').length;
  var photolist$ = $('div#photolist li');
  var p_title$ = $('div#mainphoto p#title');
  var photocount = photolist$.length;
  var nextphoto = 1;
  var info;
  var src;
  var alt;
  var nextimg;  
  var photo_link$ = $('ul.panel li a');
  var selected_photo;
  var panel_pos =0;
  var timer;
  photo_link$.click(function(){
    stop_slideshow();
    selected_photo = photo_link$.index($(this));
    info = $(photolist$.get(selected_photo)).text().split(',');
    src = $.trim(info&#91;0&#93;);
    alt = $.trim(info&#91;1&#93;);
    nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    p_title$.fadeOut(300);
    $('div#mainphoto img').before(nextimg);
    $('div#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
      p_title$.text(alt).fadeIn(200);
    });
    panel_pos = Math.floor(selected_photo / photo_per_panel);
    nextphoto = (selected_photo + 1) % photocount;  
    return false;
  });
  
  var is_stopped = false;
  $('#stop').click(function(){
    if(is_stopped) {
      timer = window.setInterval(function(){
        console.log(timer);
        showphoto();
        stop_slide = true;
      }, 3000);
      $(this).text('Stop');
      is_stopped = false;      
      var margin_left = parseInt($('#slider_inner').css('margin-left'), 10);
      animate_panel(margin_left + slider_width * (current_panel - panel_pos) );
      current_panel = panel_pos;
      icon_setup()    
      
    }else{
      console.log(timer);
      window.clearInterval(timer);
      $(this).text('Start');
      is_stopped = true;
    }    
  });
  
  
  $('#slider_prev').click(function(){
    stop_slideshow();
    if(current_panel > 0) {    
      animate_panel(parseInt($('#slider_inner').css('margin-left'), 10) + slider_width);
      current_panel --;
      icon_setup();
    }else{
      animate_panel(parseInt($('#slider_inner').css('margin-left'), 10) - (slider_width * (panel_count -1)));
      current_panel = panel_count -1;
      icon_setup();
    }
  });
  
  $('#slider_next').click(function(){
    if(stop_slide) {
      stop_slideshow();
    }
    if(current_panel < panel_count - 1) {
      animate_panel(parseInt($('#slider_inner').css('margin-left'), 10) - slider_width);
    }else{
      animate_panel(parseInt($('#slider_inner').css('margin-left'), 10) + (slider_width * (panel_count -1)));
    }
    current_panel = (current_panel + 1) % panel_count;  
    icon_setup();
  });
  
  function showphoto() {
    stop_slide = true;
    photolist$ = $('div#photolist li');
    info = $(photolist$.get(nextphoto)).text().split(',');
    src = $.trim(info&#91;0&#93;);
    alt = $.trim(info&#91;1&#93;);
    nextimg = '<img src="images/photos/slideshow/' + src + '" width="680" height="452" alt="' + alt + '">';
    p_title$.fadeOut(300);
    $('div#mainphoto img').before(nextimg);
    $('div#mainphoto img:last').fadeOut(1000, function(){
      $(this).remove();
      p_title$.text(alt).fadeIn(200);
    });
    nextphoto ++;
    if(nextphoto >= photocount ) nextphoto = 0;
    if(nextphoto % photo_per_panel == 1) {      
      stop_slide = false;
      panel_pos = (panel_pos + 1) % panel_count;
      $('#slider_next').click();    
    }    
  }
  
  function stop_slideshow() {
    if(!is_stopped) {
      console.log(timer);
      window.clearInterval(timer);
      $('#stop').text('Start');
      is_stopped = true;
    }
  }
  
  //パネルをスライドさせるアニメーション
  var default_speed = 800;
  var is_animate = false;
  function animate_panel(margin_left, speed) {
    is_animate = true;
    if(speed) { var speed = speed; }else{ var speed = default_speed; }
    prev_next$.hide();
    slider_inner$.stop().animate({
      marginLeft: margin_left + 'px'
    }, speed,
    function(){
      prev_next$.show();
      is_animate = false;
    });
  }
  //現在表示されているパネルに対応する画像を変更する処理
  function icon_setup() {
    icon_imgs.attr('src', icon_path);
    $(icon_imgs.get(current_panel)).attr('src', icon_selected_path);
  }
  
  timer = window.setInterval(function(){
    console.log(timer);
    showphoto();
    stop_slide = true;
  }, 3000);
  
  $(window).unload(function(){
    window.clearInterval(timer);
  });
  
});
</script>