イメージスライダーの仕組みを考える

2014-09-28

イメージスライダーの仕組みを考える

画像をみょーんと移動させるイメージスライダー、ホームページにインパクトを持たせたり、アピールしたいキャンペーンを効果的に見せるためなど、多くのホームページで利用されています。その多くは無償で公開されているソースを利用する事が多いと思います。これからホームページにイメージスライダーを導入しようとしている方はそれらのソースを利用する方が間違い有りません。また、CMSプラグインも多く公開されていますので、CMSで制作したホームページに導入する場合は、プラグインを使うと管理も簡単です。

ここで紹介するのは、その一歩先、「イメージスライダーってどうやって動いているのだろう?」と興味を持った方を対象にしたサンプルです。一口にイメージスライダーと言っても様々な手法が有りますので、あくまで一例と考え、スキルアップに役立てばと思います。

そもそも何で画像が動くのか

CSSに多少明るい方は、ボックスに絶対位置指定(position: absolute)を設定すると、好きな位置に表示する事が出来る事を理解していると思います。例えば左から100ピクセルの位置(left:100)というように指定します。

であるならば、この数値を少しづつ変化させれば動くという事です。

サンプル

jQuery(document).ready(function(){
  $(document).on('click','#move_btn',function(){
    $('#move').animate({left:'450px'},2000);
  });
});

ボタンを押すと2秒掛けて右に0~450px移動します。このとき、CSSはlef:0~left:450と変化していきます。

もっともシンプルだと思われる無限スライダー

先ずはサンプルをどうぞ

1~3までのボックスを無限にスライドしています。

これじゃ判り難いので、こちらのサンプルを見て下さい。

解りやすくしたサンプル

jQuery(document).ready(function(){
  
  Loop();
  
    function Loop(){
      //枠を左端に
        $('#wrap').css({left:'0'});
        
      //10秒掛けて左端からの位置をマイナスにすることで移動してるように見せる
      //10秒後にはleft:-300px;となる
      //stop()でアニメーションのキューを消さないと無限にならない
        $('#wrap').stop().animate({left:'-300px'},10000,'linear');
      
      //10秒後に自身(Loop)をもう一度実行する
        setTimeout(function(){
          Loop();
        },10000);
    };
    
  $('#wrap ul').clone().appendTo('#wrap');
});

おそらくこれが一番シンプルな無限スライダーだと思います。枠自体は3つしか無いのですが、その枠全体を自分の後ろにくっつけて(clone)、それを徐々に移動させます。3つの枠はそれぞれ幅100ピクセルなので、その3倍、300ピクセル分だけ移動したら(left:-300)最初の位置(left:0)に戻します。

つまり、無限に左に移動しているように見えますが、実は指定分だけ移動したら戻ってるだけなのですね。一瞬で戻るので目には見えません。つまり、枠が行き来してるだけです。

なぜクローンを後ろにくっつけるかというのは、3の次に1が見えないとだからです。

ちなみに縦にスライドさせるサンプルはこちら

縦に無限スライドさせてみる

プラグイン化してみる

ここまでのサンプルは、枠の幅やスピードなどをソース内に書いていますので、汎用的には使えません。これをボックスの幅など自動的に取得しつつ計算し、汎用的に利用出来るようプラグイン化します。

(function($){
   $.fn.roopSlider = function(options) {
      
      var settings = $.extend( {
                       'speed'   : 10000,
                     }, options);
                     
      var Slider = function(obj){
        var ul = $(obj).children("ul");
        var li = $(obj).children('ul').children('li');
        
        //ULの幅を得る (liの幅 * 個)
          var ul_width = li.length * li.width();
          
        //ULをslider_wrapで囲う
          $(obj).children('ul').wrapAll('');
          
        //囲ったslider_wrapの幅をULの倍にして絶対位置に指定する
          $('#slider_wrap').css({
            top: '0',
            left: '0',
            width: ul_width*2,
            height: ul.height(),
            overflow: 'hidden',
            position: 'absolute',
          });
        //ULの後ろに自身をコピーをくっつける
          ul.clone().appendTo('#slider_wrap');
        //ループ開始
          roop(ul_width);
      };
      
      var roop = function(w){
          $('#slider_wrap').css({left:'0'});
          $('#slider_wrap').stop().animate({left:'-'+w+'px'},settings.speed,'linear');
          
          setTimeout(function(){
            roop(w);
          },settings.speed);
      };

      return Slider(this);
  };
})(jQuery);

HTMLでこんな感じで呼び出す

   $(function(){
      $('#roop').roopSlider({
        'speed'   : 20000
      });
   });
</pre>
<div id="roop">
<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>
</div>
<pre>

サンプル

ボックスの幅などはCSS依存ですが、その幅を取得したり枚数を数えたりをjQueryで行います。

いやそうじゃなく一枚づつスライドさせたい

一枚づつスライドさせる場合の、おそらく一番シンプルなサンプル

これも判り難いので、解りやすくしたサンプルもどうぞ

解りやすいサンプル

これは少しだけ複雑です。

jQuery(document).ready(function(){
  
  Loop_one(0);
  
    function Loop_one(left){
      var timeOut = 3000;
      
    //left分だけ左からの距離をマイナスする
      $('#wrap').stop().animate({left:'-'+left+'px'},1000);

    //左からの距離が3枚分を超えたらアニメーションキューを全て削除
    //左からの距離を0に変更
    //このときsetTimeout分の時間が二回になるので、0秒後に変更
      if(left == 400){
        $('#wrap').queue([]); 
        $('#wrap').stop();
        
        $('#wrap').css({left:'0px'});
        left = 0;
        timeOut = 0;
      }
      
    //現在の左からの距離+100
      left = left+100;

    //指定時間後に自身を実行
      setTimeout(function(){
        Loop_one(left);
      },timeOut);
    }
    
    $('#wrap ul').clone().appendTo('#wrap');
});

ただ流れるだけのスライドは少しづつ左に移動させるだけでしたが、これはボックスの幅100ピクセルごとにアニメーションを実行し、少し停止した後、次の移動を開始します。一回目はleft:-100、二回目はleft:-200…と移動させます。3枚全部を移動させたらleft:0にして最初に戻します。このとき、溜まったアニメーションを初期化するのがミソでしょうか。

これもプラグイン化してみる

プラグイン化したサンプル

(function($){
   $.fn.roopSlider = function(options) {
      
      var settings = $.extend( {
                       'speed'   : 1000,
                       'timeOut' : 3000,
                     }, options);
                     
      var Slider = function(obj){
        var ul = $(obj).children("ul");
        var li = $(obj).children('ul').children('li');
        
        //ULの幅を得る (liの幅 * 個)
          var ul_width = li.length * li.width();
          
        //ULをslider_wrapで囲う
          $(obj).children('ul').wrapAll('');
          
         //囲ったslider_wrapの幅をULの倍にして絶対位置に指定する
          $('#slider_wrap').css({
            top: '0',
            left: '0',
            width: ul_width*2,
            height: ul.height(),
            overflow: 'hidden',
            position: 'absolute',
          });
        //ULの後ろに自身をコピーをくっつける
          ul.clone().appendTo('#slider_wrap');
          
          Loop_one(li,0);
      };
      
      var Loop_one = function(li,left){
        var timeOut  = settings.timeOut;
        var li_width = li.width();
        var len      = li.length;
        var turn     = (li_width*len)+li_width;

        //left分だけ左からの距離をマイナスする
          $('#slider_wrap').stop().animate({left:'-'+left+'px'},settings.speed);
        
        //ULの幅+LIの幅になったらアニメーションを初期化して最初に戻す
          if(left == turn){
            $('#slider_wrap').queue([]); 
            $('#slider_wrap').stop();

            $('#slider_wrap').css({left:'0px'});
            left = 0;
            timeOut = 0;
          }
          
        //現在の左からの距離+LIの幅
          left = left+li_width;
          
        //指定時間後に自身を実行
          setTimeout(function(){
            Loop_one(li,left);
          },timeOut);
        
      };
      
      return Slider(this);
  };
})(jQuery);

HTMLからはこんな感じで呼び出します。

   $(function(){
      $('#roop').roopSlider({
         'speed'   : 1000,
         'timeOut' : 5000,
      });
   });

ソースがごっそり増えていますが、やっているのは幅を取得したりLIの数を数えたりなど、そんな事です。

このプラグインを使った画像スライダー

写真使うと一気にそれっぽくなります。

写真を停止したり移動コントローラーなど付けたい

そんな貴方には無料で提供されているイメージスライダーをお薦め致します(汗

まとめると

文字だろうがボックスだろうが、position: absolute が指定されていれば、ブラウザ内のどの位置にも表示する事が出来ます。位置を決定するtopやleftの数値を少しづつ変化させれば、それは動いてるように見えます。後はそれをどの位置にどの早さでどういうタイミングで動かすかだけです。

紹介したサンプルは枠をクローンして後ろにくっつけ、一方向に移動させるだけでしたが、移動させるアイテムを分解し、配列化し、それぞれを自由に動かす事でスライダーを表現させる方法も有ると思います。

例えばスライドするのではなく、フェードイン/フェードアウトであれば画像全てを同じ位置に移動させ、順番にフェードイン/フェードアウトさせる、という処理が思い浮かびます。先ずはこれをどれだけシンプルに書けるかという事を実践し、そこから発展させていくのが良いと思います。