先日ここでレスポンシブ対応のスライドショーの試作品として
「jQueryでレスポンシブ対応のサムネイル付きクロスフェードスライドショーを作る方法 」と題して
jQueryを使ってのレスポンシブ対応のサムネイル付きクロスフェードスライドショーを
紹介しましたが、操作面に関してフリック操作には対応していなかったので、
メイン部分にフリック動作を追加してみたパターンを試しに作ってみたので紹介してみます。
jQueryでレスポンシブ対応のフリックスライダー(サムネイル付き)をシンプルに作る方法
レスポンシブ動作部分はブラウザ枠を目いっぱいで表示するタイプなので
まずは別枠表示で動作サンプルから。。
jQueryでレスポンシブ対応のフリックスライダー(サムネイル付き)をシンプルに作る方法
サンプルでは6枚の画像を自動でスライド切り替えしています。
メインビジュアル部分をフリックすることで左右に移動させることができ
下に並べてあるサムネイルをクリックすることでも
メインビジュアル部分を切り替えることができる形になっています。
(左右のNEXT/BACK透過ボタンで画像を切り替えることも可。)
サンプルでは最大幅を「800px」最小幅を「320px」として
ブラウザ幅によってスライドショー部分が拡大縮小します。(どちらの値も変更可)
簡単な動作仕様については以上になります。
全体構成についてまずはHTMLから。
◆HTML <div class="flickSlider"> <div class="flickView"> <ul> <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><!--/.flickView--> <div class="flickThumb"> <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><!--/.flickThumb--> </div><!--/.flickSlider-->
クラスやIDを付けた全体を囲う任意のブロック要素「.flickSlider」の中に
メインビジュアル部分のブロック要素「.flickView」と
サムネイル部分のブロック要素「.flickThumb」を配置し
それぞれの中に<ul>リストを使って画像を並べます。
(メインビジュアルの画像は<a>タグで囲ってありますが
リンクする必要がない場合はこのタグは不要です。)
サンプルでは画像を6つにしていますが
これを増減させるには単純に<li>の数をそれぞれ同じ個数増減するだけです。
ページネーションや左右のNEXT/BACKボタンは
スクリプト側から生成するようにしているので
HTMLには記述しません。
そしてCSSは以下の様になります。
◆CSS /* .flickSlider --------------------------- */ .flickSlider { margin: 0 auto; text-align: left; display: none; } /* .flickView --------------------------- */ .flickSlider .flickView { width: 100%; text-align: left; position: relative; overflow: hidden; } .flickSlider .flickView ul { top: 0; left: 0; width: 100%; position: absolute; overflow: hidden; } .flickSlider .flickView ul li { width: 100%; float: left; display: inline; overflow: hidden; } .flickSlider .flickView ul li img { width: 100%; } /* .flickThumb --------------------------- */ .flickSlider .flickThumb { width: 100%; overflow: hidden; } .flickSlider .flickThumb ul { width: 110%; } .flickSlider .flickThumb ul li { float: left; cursor: pointer; display: inline; } .flickSlider .flickThumb ul li img { width: 100%; } .flickSlider .flickThumb ul li.active { filter:alpha(opacity=100)!important; -moz-opacity: 1!important; opacity: 1!important; } /* sideNavi ------------------------- */ .flickSlider .btnPrev, .flickSlider .btnNext { top: 0; width: 5%; height: 100%; position: absolute; cursor: pointer; } .flickSlider .btnPrev { left: 0; background: #ccc url(../img/btnPrev.png) no-repeat center center; } .flickSlider .btnNext { right: 0; background: #ccc url(../img/btnNext.png) no-repeat center center; } /* ======================================= ClearFixElements ======================================= */ .flickSlider .flickView ul:after, .flickSlider .flickThumb ul:after { content: "."; height: 0; clear: both; display: block; visibility: hidden; } .flickSlider .flickView ul, .flickSlider .flickThumb ul { display: inline-block; overflow: hidden; }
スライドショー部分の幅や高さは
スクリプト側から制御するので
CSS側では幅100%だけ入れておきます。
ブラウザ幅の判別などもスクリプト側で制御するので
CSSでのメディアクエリー指定も特に使用しておりません。
(実際にページに組み込む際にはページのレイアウト制御等々で使用することになると思いますが)
スライド左右の透過NEXT/BACKボタン部分の
色など見た目はCSSで指定しておきます。
そして実際の動作スクリプトは以下の様になります。
◆SCRIPT <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script> $(function(){ $(window).load(function(){ var setWrap = $('.flickSlider'), setMainView = $('.flickView'), setThumbNail = $('.flickThumb'), setMaxWidth = 800, setMinWidth = 320, thumbNum = 6, thumbOpc = 0.5, scrollSpeed = 500, delayTime = 5000, easing = 'linear', sideNavi = 'on', // 'on' or 'off' autoPlay = 'on'; // 'on' or 'off' var agent = navigator.userAgent; setWrap.each(function(){ var thisObj = $(this), childMain = thisObj.find(setMainView),mainUl = childMain.find('ul'),mainLi = mainUl.find('li'),mainLiLink = mainLi.children('a'),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'), mainWidth = parseInt(childMain.css('width')),mainHeight = parseInt(childMain.css('height')),listCount = mainUl.children('li').length; thisObj.css({display:'block'}); // レスポンシブ動作メイン imgSize(); function imgSize(){ var windowWidth = parseInt($(window).width()), setUlLeft = parseInt(mainUl.css('left')), setlistWidth = parseInt(mainLi.css('width')), setLeft = setUlLeft / setlistWidth; if(windowWidth >= setMaxWidth) { thisObj.css({width:setMaxWidth}); childMain.css({width:setMaxWidth}); mainUl.css({width:((setMaxWidth)*(listCount))}); mainLi.css({width:setMaxWidth}); var listWidthA = parseInt(mainLi.css('width')), leftMax = -((listWidthA)*((listCount)-1)), baseHeight = mainLiImg.height(); childMain.css({height:baseHeight}); mainUl.css({height:baseHeight}); mainLi.css({height:baseHeight}); thumbUl.css({width:setMaxWidth}); thumbLi.css({width:((setMaxWidth)/(thumbNum)),height:''}); } else if(windowWidth < setMaxWidth) { var listWidthB = parseInt(childMain.css('width')), leftMax = -((listWidthB)*((listCount)-1)); thisObj.css({width:setMaxWidth}); if(windowWidth >= setMinWidth) { thisObj.css({width:'100%'}); childMain.css({width:'100%'}); mainUl.css({width:((listWidthB)*(listCount))}); mainLi.css({width:(listWidthB)}); } else if(windowWidth < setMinWidth) { thisObj.css({width:setMinWidth}); childMain.css({width:setMinWidth}); mainUl.css({width:((setMinWidth)*(listCount))}); mainLi.css({width:setMinWidth}); } var reHeight = mainLiImg.height(); childMain.css({height:reHeight}); mainUl.css({height:reHeight}); mainLi.css({height:reHeight}); var reWidth = setThumbNail.width(); thumbUl.css({width:reWidth}); thumbLi.css({width:((reWidth)/(thumbNum))}); } var adjLeftWidth = parseInt(mainLi.css('width')), adjLeft = adjLeftWidth * setLeft; mainUl.css({left:(adjLeft)}); } imgSize(); $(window).resize(function(){ if(!(agent.search(/iPhone/) != -1 || agent.search(/iPad/) != -1 || agent.search(/iPod/) != -1 || agent.search(/Android/) != -1)){ if(autoPlay == 'on'){clearInterval(setTimer);} imgSize(); if(autoPlay == 'on'){slideTimer();} } else { imgSize(); } }); // フリックアクション var isTouch = ('ontouchstart' in window); mainUl.on( {'touchstart mousedown': function(e){ if(mainUl.is(':animated')){ e.preventDefault(); } else { if(autoPlay == 'on'){clearInterval(setTimer);} if(!(agent.search(/iPhone/) != -1 || agent.search(/iPad/) != -1 || agent.search(/iPod/) != -1 || agent.search(/Android/) != -1)){ e.preventDefault(); } this.pageX = (isTouch ? event.changedTouches[0].pageX : e.pageX); this.leftBegin = parseInt($(this).css('left')); this.left = parseInt($(this).css('left')); this.touched = true; } },'touchmove mousemove': function(e){ if(!this.touched){return;} e.preventDefault(); this.left = this.left - (this.pageX - (isTouch ? event.changedTouches[0].pageX : e.pageX) ); this.pageX = (isTouch ? event.changedTouches[0].pageX : e.pageX); $(this).css({left:this.left}); },'touchend mouseup mouseout': function(e){ if (!this.touched) {return;} this.touched = false; var setThumbLiActive = thumbUl.children('li.active'), listWidth = parseInt(mainLi.css('width')),leftMax = -((listWidth)*((listCount)-1)); if(((this.leftBegin)-30) > this.left && (!((this.leftBegin) === (leftMax)))){ $(this).stop().animate({left:((this.leftBegin)-(listWidth))},scrollSpeed,easing); setThumbLiActive.each(function(){ $(this).removeClass('active'); $(this).next().addClass('active'); }); } else if(((this.leftBegin)+30) < this.left && (!((this.leftBegin) === 0))){ $(this).stop().animate({left:((this.leftBegin)+(listWidth))},scrollSpeed,easing); setThumbLiActive.each(function(){ $(this).removeClass('active'); $(this).prev().addClass('active'); }); } else if(this.leftBegin === 0) { $(this).stop().animate({left:'0'},scrollSpeed,easing); } else if(this.leftBegin <= leftMax) { $(this).stop().animate({left:(leftMax)},scrollSpeed,easing); } else if(this.left >= 0) { $(this).stop().animate({left:'0'},scrollSpeed); } else if(this.left <= leftMax) { $(this).stop().animate({left:(leftMax)},scrollSpeed,easing); } else { $(this).stop().animate({left:(this.leftBegin)},scrollSpeed,easing); } compBeginLeft = this.leftBegin; compThisLeft = this.left; mainLiLink.click(function(e){ if(!(compBeginLeft === compThisLeft)){ e.preventDefault(); } }); if(autoPlay == 'on'){slideTimer();} } }); // ボタン移動動作 thumbLi.click(function(){ if(autoPlay == 'on'){clearInterval(setTimer);} var listWidth = parseInt(mainLi.css('width')),leftMax = -((listWidth)*((listCount)-1)), connectCont = thumbLi.index(this); mainUl.stop().animate({left:(-(listWidth)*(connectCont))},scrollSpeed,easing); thumbLi.removeClass('active'); $(this).addClass('active'); if(autoPlay == 'on'){slideTimer();} }); thumbLiFst.addClass('active'); thumbLi.css({opacity:thumbOpc}); // サイドナビボタン(有り無し) if(sideNavi == 'on'){ childMain.append('<div class="btnPrev"></div><div class="btnNext"></div>'); var setPrev = childMain.find('.btnPrev'),setNext = childMain.find('.btnNext'),setPrevNext = childMain.find('.btnPrev,.btnNext'); setPrevNext.css({opacity:thumbOpc}); setNext.click(function(){ var setThumbLiActive = thumbUl.children('li.active'); setThumbLiActive.each(function(){ var listLengh = thumbLi.length; var listIndex = thumbLi.index(this); var listCount = listIndex+1; if(listLengh == listCount){ thumbLiFst.click(); } else { $(this).next('li').click(); } }); }); setPrev.click(function(){ var setThumbLiActive = thumbUl.children('li.active'); setThumbLiActive.each(function(){ var listLengh = thumbLi.length; var listIndex = thumbLi.index(this); var listCount = listIndex+1; if(1 == listCount){ thumbLiLst.click(); } else { $(this).prev('li').click(); } }); }); } // サムネイルマウスオーバー 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'},300); },function(){ $(this).stop().animate({opacity:thumbOpc},300); }); if(sideNavi == 'on'){ setPrevNext.hover(function(){ $(this).stop().animate({opacity:'1'},300); },function(){ $(this).stop().animate({opacity:thumbOpc},300); }); } } // 自動再生(有り無し) if(autoPlay == 'on'){ function slideTimer() { setTimer = setInterval(function(){ var setThumbLiActive = thumbUl.children('li.active'); setThumbLiActive.each(function(){ var listLengh = thumbLi.length; var listIndex = thumbLi.index(this); var listCount = listIndex+1; if(listLengh == listCount){ thumbLiFst.click(); } else { $(this).next('li').click(); } }); },delayTime); } slideTimer(); } }); }); }); </script>
スクリプト開始部分にある設定値の内容は以下の様になっています。
var setWrap = $(‘.flickSlider’) | スライダー全体を囲うブロック要素名(IDでも可) |
---|---|
setMainView = $(‘.flickView’) | メインビジュアル部分を囲うブロック要素名(IDでも可) |
setThumbNail = $(‘.flickThumb’) | サムネイル部分を囲うブロック要素名(IDでも可) |
setMaxWidth = 800 | スライダー部分の最大幅 |
setMinWidth = 320 | スライダー部分の最小幅 |
thumbNum = 6 | 一行に並べるサムネイル数 |
thumbOpc = 0.5 | サムネイルの非アクティブ時の透過度 |
scrollSpeed = 500 | スライドアニメーションスピード |
delayTime = 5000 | スライドアニメーション待機時間 |
easing = ‘linear’ | スライドアニメーションのイージング指定 |
sideNavi = ‘on’ | 左右NEXT/BACKの表示/非表示(非表示の場合は「off」等) |
autoPlay = ‘on’ | 自動切換え動作のON/OFF(自動切換えさせない場合は「off」等) |
これらの設定値を変更することでもろもろ調整が可能になっています。
スクリプト全体はあまりシンプルと言える構成ではありませんが
少しの設定値を調整するだけで簡単に設置することは可能になっているかと思います。
画像サイズ(比率)によって各エリアの幅と高さを取得して制御していることもあり
使用する(切り替える)画像は全て幅と高さを同サイズのものを使用する必要があります。
サンプルではサムネイルを1行に6つ全て並べていますが
「thumbNum = 6」を「3」とすることで1行に3つ並べて2行にわたって表示する形になります。
「sideNavi = ‘on’」を「off」とすれば左右のボタンは表示されなくなります。
(記述は「off」でなくても「on」じゃなければ何でもいいです・・・)
1ページ内に複数設置する場合は自動スライド機能は一つ目のスライダーのみにしか動作しないので
必要なければ「autoPlay = ‘on’」を「off」としていただければと思います。
(こちらも記述は「off」でなくても「on」じゃなければ何でもいいです・・・)
左右のボタン非表示、サムネイル3個2行表示、自動切換え動作を止めてスライドアニメーションにイージングを追加、各メイン画像クリックで外部リンクを追加したパターンのサンプルを別枠で表示
iPhoneやAndroid端末でも動作するハズ。
レスポンシブ対応のフリックスライダーを実装する際にぜひ。。。