先日ここで、レスポンシブ対応のスライドショーとして、
「jQueryでレスポンシブ対応のサムネイル付きクロスフェードスライドショーを作る方法」
と題して、クロスフェードで切り替わるレスポンシブスライドショーを紹介しましたが
フェード切り替えばかりじゃおもしろくないと思い
同様の構成で切り替わる際に、画像を分割してアニメーションするパターンを
実験的に作ってみたので紹介してみたいと思います。
jQueryでレスポンシブ対応の画像を分割して切り替えるスライドショーを作る実験
今回のスライダー動作はなかなか言葉では説明しずらいのでまず動作サンプルから。。。
「jQueryでレスポンシブ対応の画像を分割して切り替えるスライドショーを作る実験」サンプルを別枠で表示
サンプルでは6枚の画像を使用し、
切り替えの際には縦に20等分ストライプ上に分割した動きで切り替えています。
下に並べてあるサムネイルをクリックすることでも
メインビジュアル部分を切り替えることができる形になっています。
(左右のNEXT/BACKボタンで画像を切り替えることも可。)
サンプルでは最大幅を「800px」最小幅を「320px」として
ブラウザ幅によってスライドショー部分が拡大縮小します。(どちらの値も変更可)
簡単な動作仕様については以上になります。
全体構成についてまずはHTMLから。
◆HTML <div class="slideShow"> <div class="mainView"> <ul class="vertical"> <li><a href="#1"><img src="img/photo1.jpg" alt=""></a></li> <li><a href="#2"><img src="img/photo2.jpg" alt=""></a></li> <li><a href="#3"><img src="img/photo3.jpg" alt=""></a></li> <li><a href="#4"><img src="img/photo4.jpg" alt=""></a></li> <li><a href="#5"><img src="img/photo5.jpg" alt=""></a></li> <li><a href="#6"><img src="img/photo6.jpg" alt=""></a></li> </ul> </div><!--/.mainView--> <div class="thumbNail"> <ul> <li><img src="img/photo1.jpg" alt=""></li> <li><img src="img/photo2.jpg" alt=""></li> <li><img src="img/photo3.jpg" alt=""></li> <li><img src="img/photo4.jpg" alt=""></li> <li><img src="img/photo5.jpg" alt=""></li> <li><img src="img/photo6.jpg" alt=""></li> </ul> </div><!--/.thumbNail--> </div><!--/.slideShow-->
クラスやIDを付けた全体を囲う任意のブロック要素「.slideShow」の中に
メインビジュアル部分のブロック要素「.mainView」と
サムネイル部分のブロック要素「.thumbNail」を配置し
それぞれの中に<ul>リストを使って画像を並べます。
サンプルでは画像を6つにしていますが
これを増減させるには単純に<li>の数をそれぞれ同じ個数増減するだけです。
ページネーションや左右のNEXT/BACKボタンは
スクリプト側から生成するようにしているので
HTMLには記述しません。
<ul>にクラスを付けてありますが
「class=”vertical”」とすると縦向きストライプ分割
「class=”horizontal”」とすると横向きボーダー分割
で動作させることができるようになっています。
そしてCSSは以下の様になります。
◆CSS /* .slideShow ------------------------- */ .slideShow { margin: 0 auto; text-align: left; display: none; } /* .mainView ------------------------- */ .slideShow .mainView { width: 100%; position: relative; } .slideShow .mainView ul { width: 100%; overflow: hidden; position: relative; } .slideShow .mainView ul li { top: 0; left: 0; width: 100%; position: absolute; } /* .thumbNail ------------------------- */ .slideShow .thumbNail { width: 100%; overflow: hidden; } .slideShow .thumbNail ul { width: 110%; } .slideShow .thumbNail ul li { float: left; display: inline; overflow: hidden; cursor: pointer; } .slideShow .thumbNail ul li img { width: 100%; } .slideShow .thumbNail ul li.active { filter: alpha(opacity=100)!important; -moz-opacity: 1!important; opacity: 1!important; } /* sideNavi ------------------------- */ .slideShow .mainView .btnPrev, .slideShow .mainView .btnNext { top: 0; width: 5%; height: 100%; position: absolute; z-index: 100; } .slideShow .mainView .btnPrev { left: -5%; background: transparent url(../img/btnPrev.png) no-repeat center center; } .slideShow .mainView .btnNext { right: -5%; background: transparent url(../img/btnNext.png) no-repeat center center; } /* ======================================= ClearFixElements ======================================= */ .slideShow ul:after { content: "."; height: 0; clear: both; display: block; visibility: hidden; } .slideShow ul { display: inline-block; overflow: hidden; }
スライドショー部分の幅や高さは
スクリプト側から制御するので
CSS側では幅100%だけ入れておきます。
ブラウザ幅の判別などもスクリプト側で制御するので
CSSでのメディアクエリー指定も特に使用しておりません。
(実際にページに組み込む際にはページのレイアウト制御等々で使用することになると思いますが)
スライド左右のNEXT/BACKボタン部分の
色など見た目はCSSで指定しておきます。
そして実際の動作スクリプトは以下の様になります。
◆SCRIPT <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script> $(function(){ $(window).load(function(){ var setWrap = $('.slideShow'), setMainView = $('.mainView'), setThumbNail = $('.thumbNail'), setMaxWidth = 800, setMinWidth = 320, sliceSplit = 20, thumbNum = 6, thumbOpc = 0.5, slideFadeTime = 1000, delayTime = 5000, easing = 'linear', sideNavi = 'on', // 'on' or 'off' autoPlay = 'on'; // 'on' or 'off' setWrap.each(function(){ var thisObj = $(this), childMain = thisObj.find(setMainView),mainUl = childMain.find('ul'),mainLi = mainUl.find('li'),mainLiFst = mainUl.find('li:first'),mainLiImg = mainLi.find('img'), childThumb = thisObj.find(setThumbNail),thumbUl = childThumb.find('ul'),thumbLi = childThumb.find('li'),thumbLiFst = childThumb.find('li:first'),thumbLiLst = childThumb.find('li:last'), slicePattern = mainUl.attr('class');//縦[vertical]横[horizontal] mainLiImg.addClass('basisImg').css({width:'100%'}); thisObj.css({width:setMaxWidth,display:'block'}); // メインエリアにID設定 mainLi.each(function(i){ $(this).attr('id','view' + (i+1).toString()).css({zIndex:'98'}); mainLiFst.css({zIndex:'99'}).stop().animate({opacity:'1'},slideFadeTime,easing); }); // メイン画像をベースにエリアの幅と高さを設定 var mainLiImg = mainLi.find('img'), baseWidth = mainLiImg.width(), baseHeight = mainLiImg.height(); if(slicePattern == 'vertical'){ var sliceWidth = Math.round(setMaxWidth / sliceSplit); } else if(slicePattern == 'horizontal') { var sliceHeight = Math.round(baseHeight / sliceSplit); } // 画像をスライス if(jQuery.support.opacity){ // IE8,7,6以外 mainLi.each(function(){ var targetList = $(this), sliceImg = targetList.find('img'), imgSrc = sliceImg.attr('src'), targetLink = targetList.find('a'); sliceImg.css({visibility:'hidden'}); if(slicePattern == 'vertical'){ $.each(new Array(sliceSplit),function(i){ i = i+1; targetLink.append('<span class="slice' + i + '" style="top:0;left:' + (sliceWidth*(i-1)) + 'px;width:0;height:' + baseHeight + 'px;display:block;position:absolute;overflow:hidden;"><img src="' + imgSrc + '" style="top:0;left:-' + (sliceWidth*(i-1)) + 'px;width:' + baseWidth + 'px;height:' + baseHeight + 'px;display:block;position:absolute;"></span>'); }); var targetSlice = targetLink.find('span'); targetSlice.stop().animate({width:(sliceWidth)},slideFadeTime,easing); } else if(slicePattern == 'horizontal') { $.each(new Array(sliceSplit),function(i){ i = i+1; targetLink.append('<span class="slice' + i + '" style="top:' + (sliceHeight*(i-1)) + 'px;left:0;width:' + baseWidth + 'px;height:0;display:block;position:absolute;overflow:hidden;"><img src="' + imgSrc + '" style="top:-' + (sliceHeight*(i-1)) + 'px;left:0;width:' + baseWidth + 'px;height:' + baseHeight + 'px;display:block;position:absolute;"></span>'); }); var targetSlice = targetLink.find('span'); targetSlice.stop().animate({height:(sliceHeight)},slideFadeTime,easing); } }); } // サムネイルクリック thumbLi.click(function(){ if(!$(this).hasClass('active')){ if(autoPlay == 'on'){clearInterval(setTimer);} var connectCont = thumbLi.index(this), showCont = connectCont+1, sliceSpan = mainLi.find('span'); if(!jQuery.support.opacity){ // IE8,7,6はやむなくフェード mainUl.find('#view' + (showCont)).appendTo(mainUl).css({zIndex:'100',opacity:'0'}).stop().animate({opacity:'1'},slideFadeTime,easing); } else { if(slicePattern == 'vertical'){ animSliceWidth = sliceSpan.width(); mainUl.find('#view' + (showCont)).appendTo(mainUl).css({zIndex:'100'}).find('span').css({width:'0',opacity:'0'}).stop().animate({width:(animSliceWidth),opacity:'1'},slideFadeTime,easing); } else if(slicePattern == 'horizontal') { animSliceHeight = sliceSpan.height(); mainUl.find('#view' + (showCont)).appendTo(mainUl).css({zIndex:'100'}).find('span').css({height:'0',opacity:'0'}).stop().animate({height:(animSliceHeight),opacity:'1'},slideFadeTime,easing); } } $(this).addClass('active'); $(this).siblings().removeClass('active'); if(autoPlay == 'on'){timer();} } }); thumbLi.css({opacity:thumbOpc}); thumbLiFst.addClass('active'); // サイドナビボタン(有り無し) var agent = navigator.userAgent; if(sideNavi == 'on'){ childMain.append('<a href="javascript:void(0);" class="btnPrev"></a><a href="javascript:void(0);" class="btnNext"></a>'); var btnPrev = thisObj.find('.btnPrev'),btnNext = thisObj.find('.btnNext'),btnPrevNext = (btnPrev,btnNext); if(!(agent.search(/iPhone/) != -1 || agent.search(/iPad/) != -1 || agent.search(/iPod/) != -1 || agent.search(/Android/) != -1)){ btnPrevNext.css({opacity:thumbOpc}).hover(function(){ $(this).stop().animate({opacity:'0.9'},200); },function(){ $(this).stop().animate({opacity:thumbOpc},200); }); } else { btnPrevNext.css({opacity:thumbOpc}); } btnPrev.click(function(){switchPrev();}); btnNext.click(function(){switchNext();}); } // レスポンシブ動作メイン function imgSize(){ $('html,body').css({overflow:'hidden'}); var windowWidth = parseInt($(window).width()), resizeLink = mainLi.find('a'), resizeImg = resizeLink.find('img.basisImg'), resizeSpan = resizeLink.find('span'), resizeSpanImg = resizeSpan.find('img'); if(windowWidth >= setMaxWidth) { thisObj.css({width:setMaxWidth}); childMain.css({width:baseWidth}); mainUl.css({width:baseWidth}); mainLi.css({width:baseWidth}); resizeImg.css({width:baseWidth}); thumbLi.css({width:((setMaxWidth)/(thumbNum))}); if(sideNavi == 'on'){ btnPrevNext.css({display:'block'}); } } else if(windowWidth < setMaxWidth) { if(windowWidth >= setMinWidth) { thisObj.css({width:'100%'}); childMain.css({width:'100%'}); mainUl.css({width:'100%'}); mainLi.css({width:'100%'}); resizeImg.css({width:'100%'}); } else if(windowWidth < setMinWidth) { thisObj.css({width:setMinWidth}); childMain.css({width:setMinWidth}); mainUl.css({width:setMinWidth}); mainLi.css({width:setMinWidth}); resizeImg.css({width:setMinWidth}); } if(sideNavi == 'on'){ btnPrevNext.css({display:'none'}); } var reWidth = setThumbNail.width(); thumbLi.css({width:((reWidth)/(thumbNum))}); } var reSetImg = resizeLink.find('img.basisImg'), setImgWidth = reSetImg.width(), setImgHeight = reSetImg.height(); childMain.css({height:setImgHeight}); mainUl.css({height:setImgHeight}); mainLi.css({height:setImgHeight}); if(jQuery.support.opacity){ // IE8,7,6以外 if(slicePattern == 'vertical'){ var setSpWidth = Math.round(setImgWidth / sliceSplit); mainLi.each(function(){ $(this).find('span').each(function(i){ $(this).css({left:(setSpWidth*i),width:setSpWidth,height:setImgHeight}).children('img').css({left:-(setSpWidth*i),width:setImgWidth,height:setImgHeight}); }); }); } else if(slicePattern == 'horizontal') { setSpHeight = Math.round(setImgHeight / sliceSplit); mainLi.each(function(){ $(this).find('span').each(function(i){ $(this).css({top:(setSpHeight*i),width:setImgWidth,height:setSpHeight}).children('img').css({top:-(setSpHeight*i),width:setImgWidth,height:setImgHeight}); }); }); } } $('html,body').css({overflow:''}); } $(window).resize(function(){imgSize();}); imgSize(); // サムネイルマウスオーバー if(!(agent.search(/iPhone/) != -1 || agent.search(/iPad/) != -1 || agent.search(/iPod/) != -1 || agent.search(/Android/) != -1)){ thumbLi.hover(function(){ $(this).stop().animate({opacity:'1'},200); },function(){ $(this).stop().animate({opacity:thumbOpc},200); }); } // ボタン移動動作 function switchNext(){ var setActive = thumbUl.find('.active'); setActive.each(function(){ var listLengh = thumbLi.length, listIndex = thumbLi.index(this), listCount = listIndex+1; if(listLengh == listCount){ thumbLiFst.click(); } else { $(this).next('li').click(); } }); } function switchPrev(){ var setActive = thumbUl.find('.active'); setActive.each(function(){ var listLengh = thumbLi.length, listIndex = thumbLi.index(this), listCount = listIndex+1; if(1 == listCount){ thumbLiLst.click(); } else { $(this).prev('li').click(); } }); } // 自動再生(有り無し) if(autoPlay == 'on'){ function timer(){ setTimer = setInterval(function(){ switchNext(); },delayTime); } timer(); } }); }); }); </script>
スクリプト開始部分にある設定値の内容は以下の様になっています。
var setWrap = $(‘.slideShow’) | スライドショー全体を囲うブロック要素名(IDでも可) |
---|---|
setMainView = $(‘.mainView’) | メインビジュアル部分を囲うブロック要素名(IDでも可) |
setThumbNail = $(‘.thumbNail’) | サムネイル部分を囲うブロック要素名(IDでも可) |
setMaxWidth = 800 | スライドショー部分の最大幅 |
setMinWidth = 320 | スライドショー部分の最小幅 |
sliceSplit = 20 | 画像切り替えの際に分割させる数 |
thumbNum = 6 | 一行に並べるサムネイル数 |
thumbOpc = 0.5 | サムネイルの非アクティブ時の透過度 |
fadeTime = 700 | フェードアニメーションスピード |
delayTime = 5000 | スライドアニメーション待機時間 |
easing = ‘linear’ | スライドアニメーションイージング |
sideNavi = ‘on’ | 左右NEXT/BACKの表示/非表示(非表示の場合は「off」等) |
autoPlay = ‘on’ | 自動切換え動作のON/OFF(自動切換えさせない場合は「off」等) |
これらの設定値を変更することでもろもろ調整が可能になっています。
スクリプト全体はあまりシンプルと言える構成ではありませんが
少しの設定値を調整するだけで簡単に設置することは可能になっているかと思います。
画像サイズ(比率)によって各エリアの幅と高さを取得して制御していることもあり
使用する(切り替える)画像は全て幅と高さを同サイズのものを使用する必要があります。
サンプルでは画像切り替えの際に、全体を20分割して切り替えていますが
「sliceSplit」の値を変えることで分割させる数を設定させることができるようになっています。
※分割数を増やしすぎると動作が重くなるので適度の数で。
上で説明済みですが切り替えるパターンは
HTMLソース上で<ul>に対してクラスを
「class=”vertical”」とすると縦向きストライプ分割
「class=”horizontal”」とすると横向きボーダー分割
といった形で切り替えることが可能になっています。
サンプルではサムネイルを1行に6つ全て並べていますが
「thumbNum = 6」を「3」とすることで1行に3つ並べて2行にわたって表示する形になります。
「sideNavi = ‘on’」を「off」とすれば左右のボタンは表示されなくなります。
(記述は「off」でなくても「on」じゃなければ何でもいいです・・・)
1ページ内に複数設置する場合は自動スライド機能は一つ目のスライダーのみにしか動作しないので
必要なければ「autoPlay = ‘on’」を「off」としていただければと思います。
(こちらも記述は「off」でなくても「on」じゃなければ何でもいいです・・・)
自動切換え動作を止めて、縦向きストライプ分割と横向きボーダー分割の2パターンを並べて、スライドアニメーションにイージングを追加したパターンのサンプルを別枠で表示
対象ブラウザはIE8,7,6も視野に入れて作っていたつもりなのですが
この記事の公開日時寸前で
きちんと動作していないことに気付き(デベロッパーツールやIE TESTERでは動いていた・・・)
どうあがいてもスライス部分がこのままの構成では
うまく動作してくれないようだったので
やむを得ず強引にIE8より古いものはフェード切り替えにしてあります。。(勉強不足でした・・・)
※また改訂版を作れたら・・・
かなり強引に実験的に作ったものですが
画像を分割して切り替えるスライドショーを実装する際にぜひ。。。