半年ほど前にここで、「jQueryでブラウザウィンドウ幅めいっぱいに要素を並べたコンテンツスライダーを設置する実験」と題して、画面幅めいっぱいでのコンテンツスライダーを紹介しましたが、当時紹介したスクリプト構成だとレスポンシブには対応していませんでした。
同じ、画面幅めいっぱいでのコンテンツスライダー構成でレスポンシブ対応させたパターンを必要に駆られて作成してみたので、ここでも紹介してみたいと思います。
【2014/01/15】 スライダー部分にフリック動作を追加しました。スクリプト内の設定でフリック動作のON/OFFを設定が可能です。
jQueryでレスポンシブ対応のブラウザウィンドウ幅めいっぱいに要素を並べたコンテンツスライダーを設置する実験
このスライダーはなかなか言葉では説明しずらいのでまず動作サンプルから。
「jQueryでレスポンシブ対応のブラウザウィンドウ幅めいっぱいに要素を並べたコンテンツスライダーを設置する実験」サンプルを別枠で表示
スライダー中心にメイン表示エリアを設置して、その前後の画像は左右に透過させたような形でスライド要素をブラウザ枠めいっぱいに並べて配置しています。
ブラウザサイズがメイン表示エリアより大きい場合は、左右の透過エリアが枠めいっぱいに広がり、メイン表示より小さくなった場合には、全体がブラウザ幅に合わせた形で伸び縮みします。
左右の透過エリアはスライダーのNEXT/BACKボタンとなっているので、クリックすることで一つずつスライダーを移動させることができます。
NEXT/BACKボタンに加えて表示するコンテンツ要素分のページネーションが付加するようにしてあるので、ページネーションの各ボタンをクリックさせることで、任意のスライド番号へ移動させることも可能です。
※2014/01/15追記
スライドメイン部分をフリック動作させることで、スライド移動させることも可能です。
動作仕様については以上になります。
全体構成についてまずはHTMLから。
◆HTML <div class="wideslider"> <ul> <li><a href="#1"><img src="img/photo01.jpg" alt="" /></a></li> <li><a href="#2"><img src="img/photo02.jpg" alt="" /></a></li> <li><a href="#3"><img src="img/photo03.jpg" alt="" /></a></li> <li><a href="#4"><img src="img/photo04.jpg" alt="" /></a></li> <li><a href="#5"><img src="img/photo05.jpg" alt="" /></a></li> </ul> </div><!--/.wideslider-->
クラスやIDを付けた任意のブロック要素の中に<ul>リストを使ってコンテンツ要素(サンプル画面で言うメイン画像)を並べます。
サンプルではコンテンツ要素を5つにしていますが、これを増減させるには単純に<li>の数を増減するだけです。
前回のスライダーと違う点は<img>タグの属性には「width」と「height」属性はつけない構成にしてあります。
ページネーションや左右のNEXT/BACKボタンはスクリプト側から生成するようにしているので、HTMLには記述しません。
そしてCSSは以下の様になります。
◆CSS .wideslider { width: 100%; text-align: left; position: relative; overflow: hidden; } .wideslider ul, .wideslider ul li { float: left; display: inline; overflow: hidden; } .wideslider ul li img { width: 100%; display: none; } .wideslider_base { top: 0; position: absolute; } .wideslider_wrap { top: 0; position: absolute; overflow: hidden; } .slider_prev, .slider_next { top: 0; overflow: hidden; position: absolute; z-index: 100; cursor: pointer; } .slider_prev {background: #fff url(../img/prev.jpg) no-repeat right center;} .slider_next {background: #fff url(../img/next.jpg) no-repeat left center;} .pagination { bottom: 10px; left: 0; width: 100%; height: 15px; text-align: center; position: absolute; z-index: 200; } .pagination a { margin: 0 5px; width: 15px; height: 15px; display: inline-block; overflow: hidden; background: #333; } .pagination a.active { filter:alpha(opacity=100)!important; -moz-opacity: 1!important; opacity: 1!important; } /* ======================================= ClearFixElements ======================================= */ .wideslider ul:after { content: "."; height: 0; clear: both; display: block; visibility: hidden; } .wideslider ul { display: inline-block; overflow: hidden; }
スライド左右の透過NEXT/BACKボタン部分の色・見た目やページネーションの色味などはCSSで指定します。
スライド要素の幅や高さについてはスクリプト側で制御するので、ここでは記述していません。
そして実際の動作スクリプトは以下の様になります。
◆SCRIPT <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script> $(function(){ $setElm = $('.wideslider'); baseWidth = 800; baseHeight = 500; minWidth = 320; slideSpeed = 700; delayTime = 5000; easing = 'linear'; autoPlay = '1'; // notAutoPlay = '0' flickMove = '1'; // notFlick = '0' btnOpacity = 0.5; pnOpacity = 0.5; ua = navigator.userAgent; $(window).load(function(){ $setElm.find('img').css({display:'block'}); $setElm.each(function(){ var targetObj = $(this), wsSetTimer; targetObj.children('ul').wrapAll('<div class="wideslider_base"><div class="wideslider_wrap"></div><div class="slider_prev"></div><div class="slider_next"></div></div>'); var findBase = targetObj.find('.wideslider_base'), findWrap = targetObj.find('.wideslider_wrap'), findPrev = targetObj.find('.slider_prev'), findNext = targetObj.find('.slider_next'); var pagination = $('<div class="pagination"></div>'); targetObj.append(pagination); var baseList = findWrap.find('li'), baseListLink = findWrap.find('li').children('a'), baseListCount = findWrap.find('li').length; baseList.each(function(i){ $(this).css({width:(baseWidth),height:(baseHeight)}); pagination.append('<a href="javascript:void(0);" class="pn'+(i+1)+'"></a>'); }); var findPagi = targetObj.find('.pagination'); setSlide(); function setSlide(){ windowWidth = $(window).width(); findList = findWrap.find('li'); setParts = (findBase,findWrap,findPrev,findNext,$setElm); setWrapLeft = parseInt(findWrap.css('left')); setlistWidth = findList.find('img').width(); setLeft = setWrapLeft / setlistWidth; if(windowWidth < baseWidth){ if(windowWidth > minWidth){ findList.css({width:(windowWidth)}); var reImgHeight = findList.find('img').height(); findList.css({height:(reImgHeight)}); setParts.css({height:(reImgHeight)}); } else if(windowWidth <= minWidth){ findList.css({width:(minWidth)}); var reImgHeight = findList.find('img').height(); findList.css({height:(reImgHeight)}); setParts.css({height:(reImgHeight)}); } } else if(windowWidth >= baseWidth){ findList.css({width:(baseWidth),height:(baseHeight)}); setParts.css({height:(baseHeight)}); } setWidth = findList.find('img').width(); setHeight = findList.find('img').height(); baseWrapWidth = (setWidth)*(baseListCount); ulCount = findWrap.find('ul').length; if(ulCount == 1){ var makeClone = findWrap.children('ul'); makeClone.clone().prependTo(findWrap); makeClone.clone().appendTo(findWrap); findWrap.children('ul').eq('1').addClass('mainList'); var mainList = findWrap.find('.mainList').children('li'); mainList.eq('0').addClass('mainActive') allListCount = findWrap.find('li').length; } allLWrapWidth = (setWidth)*(allListCount), posAdjust = ((windowWidth)-(setWidth))/2; findBase.css({left:(posAdjust),width:(setWidth),height:(setHeight)}); findPrev.css({left:-(posAdjust),width:(posAdjust),height:(setHeight),opacity:(btnOpacity)}); findNext.css({right:-(posAdjust),width:(posAdjust),height:(setHeight),opacity:(btnOpacity)}); findWrap.css({width:(allLWrapWidth),height:(setHeight)}); findWrap.children('ul').css({width:(baseWrapWidth),height:(setHeight)}); posResetNext = -(baseWrapWidth)*2, posResetPrev = -(baseWrapWidth)+(setWidth); adjLeft = setWidth * setLeft; findWrap.css({left:(adjLeft)}); } findWrap.css({left:-(baseWrapWidth)}); var pnPoint = pagination.children('a'), pnFirst = pagination.children('a:first'), pnLast = pagination.children('a:last'), pnCount = pagination.children('a').length; if(ua.search(/iPhone/) != -1 || ua.search(/iPad/) != -1 || ua.search(/iPod/) != -1 || ua.search(/Android/) != -1){ pnPoint.css({opacity:(pnOpacity)}); } else { pnPoint.css({opacity:(pnOpacity)}).hover(function(){ $(this).stop().animate({opacity:'1'},300); }, function(){ $(this).stop().animate({opacity:(pnOpacity)},300); }); } pnFirst.addClass('active'); pnPoint.click(function(){ if(autoPlay == '1'){clearInterval(wsSetTimer);} var setNum = pnPoint.index(this), moveLeft = ((setWidth)*(setNum))+baseWrapWidth; findWrap.stop().animate({left: -(moveLeft)},slideSpeed,easing); pnPoint.removeClass('active'); $(this).addClass('active'); activePos(); if(autoPlay == '1'){wsTimer();} }); if(autoPlay == '1'){wsTimer();} function wsTimer(){ wsSetTimer = setInterval(function(){ findNext.click(); },delayTime); } findNext.click(function(){ findWrap.not(':animated').each(function(){ if(autoPlay == '1'){clearInterval(wsSetTimer);} var posLeft = parseInt($(findWrap).css('left')), moveLeft = ((posLeft)-(setWidth)); findWrap.stop().animate({left:(moveLeft)},slideSpeed,easing,function(){ var adjustLeft = parseInt($(findWrap).css('left')); if(adjustLeft <= posResetNext){ findWrap.css({left: -(baseWrapWidth)}); } }); var pnPointActive = pagination.children('a.active'); pnPointActive.each(function(){ var pnIndex = pnPoint.index(this), listCount = pnIndex+1; if(pnCount == listCount){ pnPointActive.removeClass('active'); pnFirst.addClass('active'); } else { pnPointActive.removeClass('active').next().addClass('active'); } }); activePos(); if(autoPlay == '1'){wsTimer();} }); }).hover(function(){ $(this).stop().animate({opacity:((btnOpacity)+0.1)},100); }, function(){ $(this).stop().animate({opacity:(btnOpacity)},100); }); findPrev.click(function(){ findWrap.not(':animated').each(function(){ if(autoPlay == '1'){clearInterval(wsSetTimer);} var posLeft = parseInt($(findWrap).css('left')), moveLeft = ((posLeft)+(setWidth)); findWrap.stop().animate({left:(moveLeft)},slideSpeed,easing,function(){ var adjustLeft = parseInt($(findWrap).css('left')), adjustLeftPrev = (posResetNext)+(setWidth); if(adjustLeft >= posResetPrev){ findWrap.css({left: (adjustLeftPrev)}); } }); var pnPointActive = pagination.children('a.active'); pnPointActive.each(function(){ var pnIndex = pnPoint.index(this), listCount = pnIndex+1; if(1 == listCount){ pnPointActive.removeClass('active'); pnLast.addClass('active'); } else { pnPointActive.removeClass('active').prev().addClass('active'); } }); activePos(); if(autoPlay == '1'){wsTimer();} }); }).hover(function(){ $(this).stop().animate({opacity:((btnOpacity)+0.1)},100); }, function(){ $(this).stop().animate({opacity:(btnOpacity)},100); }); function activePos(){ var posActive = findPagi.find('a.active'); posActive.each(function(){ var posIndex = pnPoint.index(this), setMainList = findWrap.find('.mainList').children('li'); setMainList.removeClass('mainActive').eq(posIndex).addClass('mainActive'); }); } $(window).on('resize',function(){ if(autoPlay == '1'){clearInterval(wsSetTimer);} setSlide(); if(autoPlay == '1'){wsTimer();} }).resize(); if(flickMove == '1'){ var isTouch = ('ontouchstart' in window); findWrap.on( {'touchstart mousedown': function(e){ if(findWrap.is(':animated')){ e.preventDefault(); } else { if(autoPlay == '1'){clearInterval(wsSetTimer);} if(!(ua.search(/iPhone/) != -1 || ua.search(/iPad/) != -1 || ua.search(/iPod/) != -1 || ua.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 = pagination.children('a.active'), listWidth = parseInt(baseList.css('width')),leftMax = -((listWidth)*((baseListCount)-1)); if(((this.leftBegin)-30) > this.left && (!((this.leftBegin) === (leftMax)))){ $(this).stop().animate({left:((this.leftBegin)-(listWidth))},slideSpeed,easing,function(){ var adjustLeft = parseInt($(findWrap).css('left')); if(adjustLeft <= posResetNext){ findWrap.css({left: -(baseWrapWidth)}); } }); setThumbLiActive.each(function(){ var pnIndex = pnPoint.index(this), listCount = pnIndex+1; if(pnCount == listCount){ setThumbLiActive.removeClass('active'); pnFirst.addClass('active'); } else { setThumbLiActive.removeClass('active').next().addClass('active'); } }); activePos(); } else if(((this.leftBegin)+30) < this.left && (!((this.leftBegin) === 0))){ $(this).stop().animate({left:((this.leftBegin)+(listWidth))},slideSpeed,easing,function(){ var adjustLeft = parseInt($(findWrap).css('left')), adjustLeftPrev = (posResetNext)+(setWidth); if(adjustLeft >= posResetPrev){ findWrap.css({left: (adjustLeftPrev)}); } }); setThumbLiActive.each(function(){ var pnIndex = pnPoint.index(this), listCount = pnIndex+1; if(1 == listCount){ setThumbLiActive.removeClass('active'); pnLast.addClass('active'); } else { setThumbLiActive.removeClass('active').prev().addClass('active'); } }); activePos(); } else { $(this).stop().animate({left:(this.leftBegin)},slideSpeed,easing); } compBeginLeft = this.leftBegin; compThisLeft = this.left; baseListLink.click(function(e){ if(!(compBeginLeft == compThisLeft)){ e.preventDefault(); } }); if(autoPlay == '1'){wsTimer();} } }); } setTimeout(function(){setSlide();},500); }); }); }); </script>
スクリプト開始部分にある設定値の内容は以下の様になっています。
$setElm = $(‘.wideslider’) | 対象にするブロック要素名(IDでも可) |
---|---|
baseWidth = 800 | スライドさせるコンテンツ要素の幅 |
baseHeight = 500 | スライドさせるコンテンツ要素の高さ |
minWidth = 320 | コンテンツ要素の最小幅 |
slideSpeed = 500 | スライドアニメーションスピード |
delayTime = 5000 | スライドアニメーション待機時間 |
easing = ‘linear’ | スライドアニメーションイージング |
autoPlay = ‘1’ | 自動スライドON/OFF(ON = 1 , OFF = 0) |
flickMove = ‘1’ | フリック動作ON/OFF(ON = 1 , OFF = 0) |
btnOpacity = 0.5 | 左右のNEXT/BACKエリアの透過具合 |
pnOpacity = 0.5 | ページネーションの透過具合 |
これらの設定値を変更することでもろもろ調整が可能になっています。
スクリプト全体はあまりシンプルと言える構成ではありませんが、少しの設定値を調整するだけで簡単に設置することは可能になっているかと思います。
設置の際の簡単な注意点としては、画像サイズ(比率)によって各エリアの幅と高さを取得して制御していることもあり、使用する(スライドさせる)画像は全て幅と高さを同サイズのものを使用する必要があります。
HTML構成上、このコンテンツスライダー要素全体を囲うブロック要素があった場合は、その要素の幅は100%になっている必要があります。
スライドアニメーション実行中にブラウザ幅を変えられたりするとスライド位置がおかしくなることがありますが、ページネーションをクリックする、もしくは自動スライド機能でスライド全体が一周すると正常に位置が調整されるようになっています。
カスタマイズに関しては、jQueryイージングプラグインを使って簡単にスライドアニメーション動作に抑揚をつけたり、CSSを少し調整することでページネーションをスライダーエリアから外へ出すことなども可能です。
ページネーションについては、生成される際に個別に「.pn(n)」のクラスが付与されるようになっているので、CSS側からそれらのクラスに対して背景画像を指定することで、サムネイル画像として表示させることも可能です。
※スライド要素が5つの場合はページネーションのクラスは「.pn1」 ~ 「.pn5」という風に個別に付与されます。
jQueryイージングプラグインを使ってスライドアニメーションにイージング処理をつけて、ページネーションをスライドエリアの外にしてサムネイル表示にした場合は、以下の様な見た目にすることができます。
jQueryイージング+ページネーションをエリア外+サムネイル表示にしたパターンのサンプルを別枠で表示
ブラウザの幅をめいっぱい使ってスライダーを設置することで、ダイナミックな演出を組み込むことが可能かと思っています。
レスポンシブ対応させたブラウザウィンドウ幅めいっぱいのコンテンツスライダーを実装する際にぜひ。。。