今回のチュートリアルでは動きのある画像スライダーをご紹介したいと思います。メインの選択画像をスライドさせる時に、立体感を出す為複数の背景レイヤーにパララックス(視差)の原理を使用しています。こうすることにより、画像が切り替わる時に素晴らしい奥行き感を実現することが出来ます。
この記事は、http://tympanus.net/codrops/の許可を得て、翻訳しています。一部変更して翻訳している部分もある場合があります。オリジナルの記事はここよりご覧いただけます。
デモ内で使用しているきれいな画像はTetsumoさんのFlickrからお借りしています。
それでは早速始めましょう!
HTMLマークアップ
HTMLの構造としては、まず一番外側の大本のコンテイナーに「pxs_container」というクラスを設定しています。パララックス効果を実現させるためには3枚の背景が必要になるので、背景にもそれぞれラッパーを追加します。奥行きを持たせる為にそれぞれの背景画像が移動するのを「見える」ようにしたいので、透過度を加えます。
それからローディング要素と、フルサイズ画像とサムネイル画像用にULリストを2つ加えます。最後にナビゲーション要素も追加します。
<div id="pxs_container" class="pxs_container"> <div class="pxs_bg"> <div class="pxs_bg1"></div> <div class="pxs_bg2"></div> <div class="pxs_bg3"></div> </div> <div class="pxs_loading">Loading images...</div> <div class="pxs_slider_wrapper"> <ul class="pxs_slider"> <li><img src="images/1.jpg" alt="First Image" /></li> <li><img src="images/2.jpg" alt="Second Image" /></li> ... </ul> <div class="pxs_navigation"> <span class="pxs_next"></span> <span class="pxs_prev"></span> </div> <ul class="pxs_thumbnails"> <li><img src="images/thumbs/1.jpg" alt="First Image" /></li> <li><img src="images/thumbs/2.jpg" alt="Second Image" /></li> ... </ul> </div> </div>
さて、次にスタイリングを見てみましょう。
CSSスタイリング
まずは大本のメインコンテナーの設定からです。スライダーの幅はページ全体を利用したいので100%に設定します。メインコンテナー内部の要素は全て絶対配置させるので、メインコンテナーは相対配置にしておきます。(内部の要素はページに対してではなくコンテナを基準にして配置したい感じです。)
.pxs_container{ width:100%; height:420px; position:relative; border-top:7px solid #333; border-bottom:7px solid #333; overflow:hidden; -moz-box-shadow:0px 0px 7px #000; -webkit-box-shadow:0px 0px 7px #000; box-shadow:0px 0px 7px #000; }
パララックス用の背景画像をラッピングしているDIV要素には、「背景画像繰り返し」の設定をします。ここで使用している画像はシンプルなグラデーション画像です。
.pxs_bg{ background:transparent url(../images/bg.png) repeat top left; }
ラッパー内部のDIVタグは、以下のスタイルプロパティを共通で設定します。
.pxs_bg div{ position:absolute; top:0px; left:0px; height:420px; background-repeat:repeat; background-position:top left; background-color:transparent; }
幅はJavaScriptで動的に設定します。背景DIVタグ1つ1つにそれぞれ異なる背景画像を割り振っています。
.pxs_bg .pxs_bg1{ background-image:url(../images/bg1.png); } .pxs_bg .pxs_bg2{ background-image:url(../images/bg2.png); } .pxs_bg .pxs_bg3{ background-image:url(../images/bg3.png); }
残りの要素も動的に設定するのですが、それはまた後でご説明するとして、まずは他の要素をのスタイリングを確認しましょう。
下記のラッパーは、初めは見えない状態にセットしてあります。
.pxs_slider_wrapper{ display:none; }
この中には最初にロードしておきたい要素全てが含まれていて、ロードが完了してから初めてラッパーとコンテンツが表示されるようにしたいのです。
2つのULリストもリセットしておきましょう。
.pxs_container ul{ margin:0px; padding:0px; list-style:none; }
このスライダー用リストでのこだわりは、LI要素内のコンテンツをページ幅全体に表示させることです。なので実際画面上で表示されているスライダーは、1つのLI要素の全体像なんです。ULリストの幅は、Window幅と画像の数に応じて動的に変化するようにしてあります。LIタグを「float:left;」に設定して動的なUL幅を用意することで、LI要素はきちんと横並びに整列してくれます。
ul.pxs_slider{ position:absolute; left:0px; top:0px; height:420px; } ul.pxs_slider li{ height:420px; float:left; position:relative; }
LIタグ内のスライド画像は、左右にオートマージンを設定して水平方向でセンタリングさせています。
ul.pxs_slider li img{ display:block; margin:35px auto 0px auto; -moz-box-shadow:0px 0px 7px #222; -webkit-box-shadow:0px 0px 7px #222; box-shadow:0px 0px 7px #222; border: 8px solid transparent; -moz-border-radius:4px; -webkit-border-radius:4px; border-radius:4px; }
透明のボーダーとボックスシャドウを追加して、画像の周りにガラス枠があるように演出します。
サムネイルリストも絶対配置です。「left:50%;」を設定しているのは、JavaScriptにより動的幅やレフトマージンが負の値になってもきちんとセンタリングさせたいからです。
ul.pxs_thumbnails{ height:35px; position:absolute; top:320px; left:50%; } ul.pxs_thumbnails li{ position:absolute; display:block; }
サムネイルの周りには白い枠線を加えて、自然なボックスシャドウも追加しています。
ul.pxs_thumbnails li img{ border: 5px solid #FFFFFF; -moz-box-shadow:1px 1px 7px #555; -webkit-box-shadow:1px 1px 7px #555; box-shadow:1px 1px 7px #555; cursor:pointer; display:block; opacity:0.7; }
選択されているサムネイル画像は、不透明にしてクッキリ表示させています。
ul.pxs_thumbnails li.selected img{ opacity:1.0; }
2つのナビゲーションスパンには以下の様に共通の設定を施します。
.pxs_navigation span{ position:absolute; width:30px; height:60px; -moz-box-shadow:0px 0px 2px #000; -webkit-box-shadow:0px 0px 2px #000; box-shadow:0px 0px 2px #000; top:145px; opacity:0.6; -moz-border-radius:4px; -webkit-border-radius:4px; border-radius:4px; cursor:pointer; } .pxs_navigation span:hover{ opacity:0.9; }
それから矢印を加えます。
.pxs_navigation span.pxs_prev{ background:#000 url(../images/prev.png) no-repeat center center; } .pxs_navigation span.pxs_next{ background:#000 url(../images/next.png) no-repeat center center; }
画像の左端と右端に表示させたいので、左矢印(pxs_prev)右矢印(pxs_next)要素も動的に設定します。
最後にローディング部分がきちんと真ん中に表示されるように設定してお終いです。
.pxs_loading{ color:#fff; font-size:20px; padding:15px 15px 15px 50px; position:absolute; background:#333 url(../images/ajax-loader.gif) no-repeat 10px 50%; -moz-border-radius:15px; -webkit-border-radius:15px; border-radius:15px; opacity:0.7; width:180px; position:absolute; top:150px; left:50%; margin-left:-90px; }
これでスタイリングは完成です。最後にjQueryを仕上げましょう!
JavaScript
今回のスライダーで大切なのは勿論画像をスライドさせることなのですが、それだけではなくて奥行きを持たせる為に3枚の背景画像をそれぞれ別々にアニメーション化させる必要があります。例えば次の画像に移動する時、スライダー用のULタグの左の値はWindow幅分マイナスに差し引かれます(すなわちこれが一つのLIタグ分の幅と言う事になります。)。それから一番手前の背景用のDIVタグもアニメーション化させるのですが、これはWindow幅の半分しか動かしません。2枚目の背景画像はWindow幅の1/4だけ・・・とそんな感じになります。なので奥の背景画像ほど、動く幅は少なくなるというのがパララックスの原理の基本です。
スクリプト外でプラグインを作成したいので、先にいくつかのオプションを定義しておきましょう。手始めに最も重要な要素をキャッシュします。
(function($) { $.fn.parallaxSlider = function(options) { var opts = $.extend({}, $.fn.parallaxSlider.defaults, options); return this.each(function() { var $pxs_container = $(this), o = $.meta ? $.extend({}, opts, $pxs_container.data()) : opts; //the main slider var $pxs_slider = $('.pxs_slider',$pxs_container), //the elements in the slider $elems = $pxs_slider.children(), //total number of elements total_elems = $elems.length, //the navigation buttons $pxs_next = $('.pxs_next',$pxs_container), $pxs_prev = $('.pxs_prev',$pxs_container), //the bg images $pxs_bg1 = $('.pxs_bg1',$pxs_container), $pxs_bg2 = $('.pxs_bg2',$pxs_container), $pxs_bg3 = $('.pxs_bg3',$pxs_container), //current image current = 0, //the thumbs container $pxs_thumbnails = $('.pxs_thumbnails',$pxs_container), //the thumbs $thumbs = $pxs_thumbnails.children(), //the interval for the autoplay mode slideshow, //the loading image $pxs_loading = $('.pxs_loading',$pxs_container), $pxs_slider_wrapper = $('.pxs_slider_wrapper',$pxs_container); //first, preload all the images var loaded = 0, $images = $pxs_slider_wrapper.find('img'); $images.each(function(){ var $img = $(this); $('<img/>').load(function(){ ++loaded; if(loaded == total_elems*2){ $pxs_loading.hide(); $pxs_slider_wrapper.show(); //width of an image //(assuming all images have the same sizes) var one_image_w = $pxs_slider.find('img:first').width(); /* set the width of the slider, of each one of its elements, and of the navigation buttons */ setWidths($pxs_slider, $elems, total_elems, $pxs_bg1, $pxs_bg2, $pxs_bg3, one_image_w, $pxs_next, $pxs_prev); /* set the widths of the thumbs and spread them evenly */ $pxs_thumbnails.css({ 'width' : one_image_w + 'px', 'margin-left' : -one_image_w/2 + 'px' }); var spaces = one_image_w/(total_elems+1); $thumbs.each(function(i){ var $this = $(this); var left = spaces*(i+1) - $this.width()/2; $this.css('left',left+'px'); if(o.thumbRotation){ var angle = Math.floor(Math.random()*41)-20; $this.css({ '-moz-transform' : 'rotate('+ angle +'deg)', '-webkit-transform' : 'rotate('+ angle +'deg)', 'transform' : 'rotate('+ angle +'deg)' }); } //hovering the thumbs animates them up and down $this.bind('mouseenter',function(){ $(this).stop().animate({top:'-10px'},100); }).bind('mouseleave',function(){ $(this).stop().animate({top:'0px'},100); }); }); //make the first thumb to be selected highlight($thumbs.eq(0)); //slide, when clicking the navigation buttons $pxs_next.bind('click',function(){ ++current; if(current >= total_elems) if(o.circular) current = 0; else{ --current; return false; } highlight($thumbs.eq(current)); slide(current, $pxs_slider, $pxs_bg3, $pxs_bg2, $pxs_bg1, o.speed, o.easing, o.easingBg); }); $pxs_prev.bind('click',function(){ --current; if(current < 0) if(o.circular) current = total_elems - 1; else{ ++current; return false; } highlight($thumbs.eq(current)); slide(current, $pxs_slider, $pxs_bg3, $pxs_bg2, $pxs_bg1, o.speed, o.easing, o.easingBg); }); /* clicking a thumb will slide to the respective image */ $thumbs.bind('click',function(){ var $thumb = $(this); highlight($thumb); //if autoplay interrupt when user clicks if(o.auto) clearInterval(slideshow); current = $thumb.index(); slide(current, $pxs_slider, $pxs_bg3, $pxs_bg2, $pxs_bg1, o.speed, o.easing, o.easingBg); }); /* activate the autoplay mode if that option was specified */ if(o.auto != 0){ o.circular = true; slideshow = setInterval(function(){ $pxs_next.trigger('click'); },o.auto); } /* when resizing the window, we need to recalculate the widths of the slider elements, based on the new window width; we need to slide again to the current one, since the left of the slider is no longer correct */ $(window).resize(function(){ w_w = $(window).width(); setWidths( $pxs_slider, $elems, total_elems, $pxs_bg1, $pxs_bg2, $pxs_bg3, one_image_w, $pxs_next, $pxs_prev ); slide( current, $pxs_slider, $pxs_bg3, $pxs_bg2, $pxs_bg1, 1, o.easing, o.easingBg ); }); } }).error(function(){ alert('here') }).attr('src',$img.attr('src')); }); }); }; //the current window width var w_w = $(window).width(); var slide = function(current, $pxs_slider, $pxs_bg3, $pxs_bg2, $pxs_bg1, speed, easing, easingBg){ var slide_to = parseInt(-w_w * current); $pxs_slider.stop().animate({ left : slide_to + 'px' },speed, easing); $pxs_bg3.stop().animate({ left : slide_to/2 + 'px' },speed, easingBg); $pxs_bg2.stop().animate({ left : slide_to/4 + 'px' },speed, easingBg); $pxs_bg1.stop().animate({ left : slide_to/8 + 'px' },speed, easingBg); } var highlight = function($elem){ $elem.siblings().removeClass('selected'); $elem.addClass('selected'); } var setWidths = function($pxs_slider, $elems, total_elems, $pxs_bg1, $pxs_bg2, $pxs_bg3, one_image_w, $pxs_next, $pxs_prev){ /* the width of the slider is the window width times the total number of elements in the slider */ var pxs_slider_w = w_w * total_elems; $pxs_slider.width(pxs_slider_w + 'px'); //each element will have a width = windows width $elems.width(w_w + 'px'); /* we also set the width of each bg image div. The value is the same calculated for the pxs_slider */ $pxs_bg1.width(pxs_slider_w + 'px'); $pxs_bg2.width(pxs_slider_w + 'px'); $pxs_bg3.width(pxs_slider_w + 'px'); /* both, the right and left of the navigation next and previous buttons will be: windowWidth/2 - imgWidth/2 + some margin (not to touch the image borders) */ var position_nav = w_w/2 - one_image_w/2 + 3; $pxs_next.css('right', position_nav + 'px'); $pxs_prev.css('left', position_nav + 'px'); } $.fn.parallaxSlider.defaults = { auto : 0, speed : 1000, easing : 'jswing', easingBg : 'jswing', circular : true, thumbRotation : true }; //easeInOutExpo,easeInBack })(jQuery);
スライダー用には以下のスクリプトを追加します。
$(function() { var $pxs_container = $('#pxs_container'); $pxs_container.parallaxSlider(); });
スライダー用のオプションはこんな感じになります。
- auto: 周期的にコンテンツをスライドさせる秒数(0の場合は自動再生は無効)
- speed: 各スライドのアニメーション速度
- easing: スライドアニメーションのイージング(緩和)効果
- easingBg: 背景アニメーションのイージング効果
- circular: 円形スライダー
- thumbRotation: サムネイルがランダムに回転
以上で完成です!今回のチュートリアル、お楽しみいただけましたか?是非活用してみてくださいね。