Slickによる「スライド式サムネイル付の画像スライダー」の実装例(asNavFor 未使用)


Slickを使用した「サムネイル付の画像スライダー」の実装として「オプション:asNavFor」を使用する方法が考えられるが、メインの画像スライダーに連動してサムネイル側もスライドされてしまう。
通常はこれでも問題はないが、サムネイルの数が表示件数以下でスライドはさせたくない場合でも適用されてしまう。
 
回避策として以下で紹介されているような方法もあるが、いずれもサムネイル側を完全固定しなければならない。
https://www.nowte.net/article/1330/
https://takblog.site/web/?p=144
 

そこで、「オプション:asNavFor」を使用しないで、メインの画像スライダーとサムネイルのスライダーを連動させる方法を考案。
これにより、以下のようなスライダーが実装可能になる。

  • メインの画像スライダーが切り替わる毎に、サムネイル側もアクション
  • サムネイルが既に表示領域内にある場合は、スライドさせずにアクティブ化のみ
  • サムネイルが表示領域外の場合、表示件数分毎にスライドさせて、指定のサムネイルをアクティブ化
  • サムネイルの数が表示件数以下の場合は完全固定

 

実装デモ


 

実例

https://hagiharaseizosho.shop/products/%E3%81%84%E8%8D%89%E3%83%A9%E3%82%B0
 

JavaScript(jQuery)

jQuery(document).ready(function($){
      if($(“#imgViewer__main > *”).length > 1){

            // メインスライダーに設定するSlickオプション
            // ※予め変数化しておくと便利
            // ※「asNavFor」を設定しないのがミソ
            var mainConfig = {
                  fade: true,
                  autoplay: true,
                  speed: 1000,
                  infinite: true,
                  slidesToShow: 1,
                  swipe: true,
                  lazyLoad: ‘progressive’,
                  responsive: [
                        {
                              breakpoint: 992,
                              settings: {
                                    arrows: false,
                                    fade: false
                              }
                        }
                  ]
            };

            // メインスライダーのアクション時(厳密にはアクション直前)に実行
            // ※必ずSlick実装の前に書く
            $(“#imgViewer__main”).on(“beforeChange”, function(event, slick, currentSlide, nextSlide){

                  // アクションを起こす毎に、サムネイルのアクティブ化切り替え
                  if(!$(“#imgViewer__thumbnail .carousel-item[data-slick-index='” + nextSlide + “‘]”).hasClass(“active”)){
                        $(“#imgViewer__thumbnail .carousel-item.active”).removeClass(“active”);
                        $(“#imgViewer__thumbnail .carousel-item[data-slick-index='” + nextSlide + “‘]”).addClass(“active”);
                  }

                  // サムネイルスライダーに設定されたSlickの各種オプション値を取得
                  var slickData = $(“#imgViewer__thumbnail”).get(0).slick;

                  // サムネイルスライダーに適用されている表示カラム数
                  var slidesToShow = slickData.options.slidesToShow;

                  // サムネイルスライダーのターゲットを決定(結果的に、条件一致の場合のみスライドアクションが発生)
                  // ターゲットのindex番号(初期値)※条件一致で、0以上になる
                  var nextToSlide = -1;
                  
                  // メインスライダーで1番目(index:0)の画像が指定されている場合は、サムネイルスライダーの方も強制的に index:0 を指定する
                  if(nextSlide == 0){
                        nextToSlide = 0;

                  // それ以外の場合は条件指定
                  } else {

                        // メインスライダーで、表示中の画像が先頭ではなく、かつ「slick-next」がクリックされた場合
                        if(nextSlide > currentSlide && currentSlide > 0){

                              // サムネイル側で、表示領域分の先頭にあたる場合は、サムネイルのターゲットを指定
                              // (次のindex ÷ 表示カラム数 の余りがゼロ)
                              if(nextSlide % slidesToShow == 0){
                                    nextToSlide = nextSlide;
                              }

                        // それ以外はさらに条件指定
                        } else {

                              // 表示中の画像が末尾にあたる (次のindex + 1 が、画像件数と一致)
                              // または、表示領域分の末尾にあたる場合は、サムネイルのターゲットを指定
                              // ( (次のindex + 1) ÷ 表示カラム数 の余りがゼロ )
                              if(nextSlide + 1 == slickData.slideCount || (nextSlide + 1 != slickData.slideCount && (nextSlide + 1) % slidesToShow == 0)) {

                                    // サムネイル中のターゲットが含まれる表示領域分の先頭のindex番号を指定
                                    nextToSlide = Math.floor(nextSlide / slidesToShow) * slidesToShow;
                              }
                        }
                  }

                  // 上記条件で、ターゲット指定がある場合
                  if(nextToSlide >= 0){

                        // ・・・で、かつスライド済でない場合のみ、サムネイルをスライドさせる
                        if(nextToSlide != slickData.currentSlide){
                              $(“#imgViewer__thumbnail”).slick(“slickGoTo”, nextToSlide, false);
                        }
                  }
            });

            // メインスライダーにslickを実装
            $(“#imgViewer__main”).slick(mainConfig);

            // サムネイルスライダーに設定するSlickオプション
            // ※「asNavFor」を設定しないのがミソ
            var subConfig = {
                  autoplay: false,
                  speed: 1000,
                  infinite: true,
                  appendArrows: ‘#imgViewer > .carousel-outline__thumbnail’,
                  slidesToShow: 6,
                  slidesToScroll: 6,
                  swipe: true,
                  lazyLoad: ‘progressive’,
                  responsive: [
                        {
                              breakpoint: 992,
                              settings: {
                                    arrows: false,
                                    slidesToShow: 4,
                                    slidesToScroll: 4
                              }
                        },
                        {
                              breakpoint: 768,
                              settings: {
                                    arrows: false,
                                    slidesToShow: 3,
                                    slidesToScroll: 3
                              }
                        }
                  ]
            };

            // サムネイルスライダーにslickを実装
            $(“#imgViewer__thumbnail”).slick(subConfig);

            // サムネイルがクリックされた際に、メインスライダーを切り替える
            $(“#imgViewer__thumbnail .carousel-item > .img-wrap”).on(“click”, function(){
                  var index = $(this).parent().attr(“data-slick-index”);
                  $(“#imgViewer__main”).slick(“slickGoTo”, index, false);
            });
      }
});
      

HTML

<div id=”imgViewer”>
      <div class=”carousel-wrap__main”>
            <div id=”imgViewer__main” class=”clearfix”>
                  <div class=”carousel-item”>
                        <div class=”img-wrap”>
                              <div class=”img”></div>
                        </div>
                  </div>
                  <div class=”carousel-item”>
                        <div class=”img-wrap”>
                              <div class=”img”></div>
                        </div>
                  </div>
                  <div class=”carousel-item”>
                        <div class=”img-wrap”>
                              <div class=”img”></div>
                        </div>
                  </div>
                  <div class=”carousel-item”>
                        <div class=”img-wrap”>
                              <div class=”img”></div>
                        </div>
                  </div>
                  <div class=”carousel-item”>
                        <div class=”img-wrap”>
                              <div class=”img”></div>
                        </div>
                  </div>
            </div>
      </div>
      <div class=”carousel-outline__thumbnail”>
            <div class=”carousel-wrap__thumbnail”>
                  <div id=”imgViewer__thumbnail” class=”clearfix”>
                        <div class=”carousel-item”>
                              <div class=”img-wrap”>
                                    <div class=”img”></div>
                              </div>
                        </div>
                        <div class=”carousel-item”>
                              <div class=”img-wrap”>
                                    <div class=”img”></div>
                              </div>
                        </div>
                        <div class=”carousel-item”>
                              <div class=”img-wrap”>
                                    <div class=”img”></div>
                              </div>
                        </div>
                        <div class=”carousel-item”>
                              <div class=”img-wrap”>
                                    <div class=”img”></div>
                              </div>
                        </div>
                        <div class=”carousel-item”>
                              <div class=”img-wrap”>
                                    <div class=”img”></div>
                              </div>
                        </div>
                  </div>
            </div>
      </div>
</div>
      

SCSS

#imgViewer {

      .carousel {
            &-wrap {
                  &__main {
                        position: relative;
                        overflow: hidden;
                        padding-top: 56.25%;
                  }
                  &__thumbnail {
                        position: relative;
                        overflow: hidden;
                  }
            }
            &-outline__thumbnail {
                  > .slick-arrow {
                        width: 40px;
                        height: 80px;
                        &.slick-prev { left: -47px; }
                        &.slick-next { right: -47px; }
                        &:before, &:after {
                              width: 20px;
                        }
                  }
            }
            &-item {
                  > .img-wrap {
                        > .img {
                              &:not(div) {
                                    display: block;
                              }
                              position: relative;
                              overflow: hidden;
                              width: 100%;
                              height: 0;
                              padding-top: 56.25%;
                              font-size: 0.5em;
                              text-align: left;
                              text-indent: -9999px;
                              background-color: #ccc;
                              background-position: center;
                              background-repeat: no-repeat;
                              background-size: contain;
                        }
                  }
            }
      }

      .slick-arrow {
            position: absolute;
            top: 50%;
            border: none;
            background: none;
            outline: 0;
            width: 60px;
            height: 90px;
            font-size: 0.5em;
            text-align: left;
            text-indent: -9999px;
            transform: translateY(-50%);
            z-index: 10;
            &:before, &:after {
                  position: absolute;
                  top: 50%;
                  left: 50%;
                  width: 30px;
                  height: 1px;
                  background-color: #666;
                  content: “”;
            }
            @media screen and (min-width: 992px) {
                  width: 80px;
                  height: 120px;
                  &:before, &:after {
                        width: 40px;
                  }
            }
            &.slick-prev {
                  left: 0;
                  &:before, &:after {
                        transform-origin: left center;
                  }
                  &:before {
                        transform: translate(-50%, -50%) rotate(45deg);
                  }
                  &:after {
                        transform: translate(-50%, -50%) rotate(-45deg);
                  }
            }
            &.slick-next {
                  right: 0;
                  &:before, &:after {
                        transform-origin: right center;
                  }
                  &:before {
                        transform: translate(-50%, -50%) rotate(45deg);
                  }
                  &:after {
                        transform: translate(-50%, -50%) rotate(-45deg);
                  }
            }
      }

      &__main {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            width: 100%;
            height: 100%;

            > .carousel-item {
                  &:first-child:not([class*=’slick’]) {
                        z-index: 10;
                  }
                  &:not(:first-child):not([class*=’slick’]) {
                        position: absolute !important;
                        top: 0;
                        left: 0;
                        opacity: 0;
                  }
            }
      }
      
      &__thumbnail {
            margin: 4px -2px 0;
            .carousel-item {
                  padding-left: 2px;
                  padding-right: 2px;
                  > .img-wrap {
                        transition: 0.3s;
                        cursor: pointer;
                        > .img {
                              background-color: #333;
                        }
                  }
                  &:not(.active):not(:hover):not(:focus) > .img-wrap {
                        opacity: 0.4;
                  }
            }
            @media screen and (min-width: 768px) {
                  margin: 14px -7px 0;
                  .carousel-item {
                        padding-left: 7px;
                        padding-right: 7px;
                  }
            }
      }

      /* ↓ Slick適用前の調整(段落ち防止処置) ↓ */

      &__main > .carousel-item {
            &:first-child:not([class*=’slick’]) {
                  z-index: 10;
            }
            &:not(:first-child):not([class*=’slick’]) {
                  position: absolute !important;
                  top: 0;
                  left: 0;
                  opacity: 0;
            }
      }

      &__thumbnail > .carousel-item {
            float: left;
            width: 33.3333333%;

            @media screen and (min-width: 768px) {
                  width: 25%;
            }
            @media screen and (min-width: 768px) and (max-width: 991px) {
                  &:nth-child(n+5) { display:none !important; }
            }
            @media screen and (min-width: 992px) {
                  width: 16.6666666%;
                  &:nth-child(n+7) { display:none !important; }
            }
            @media screen and (max-width: 767px) {
                  &:nth-child(n+4) { display:none !important; }
            }
      }
}
      

CSS

#imgViewer .carousel-wrap__main {
      overflow: hidden;
      padding-top: 56.25%;
}

#imgViewer .carousel-wrap__thumbnail {
      overflow: hidden;
}

#imgViewer .carousel-outline__thumbnail > .slick-arrow {
      width: 40px;
      height: 80px;
}

#imgViewer .carousel-outline__thumbnail > .slick-arrow.slick-prev {
      left: -47px;
}

#imgViewer .carousel-outline__thumbnail > .slick-arrow.slick-next {
      right: -47px;
}

#imgViewer .carousel-outline__thumbnail > .slick-arrow:before, #imgViewer .carousel-outline__thumbnail > .slick-arrow:after {
      width: 20px;
}

#imgViewer .carousel-item > .img-wrap > .img {
      position: relative;
      overflow: hidden;
      width: 100%;
      height: 0;
      padding-top: 56.25%;
      font-size: 0.5em;
      text-align: left;
      text-indent: -9999px;
      background: #ccc center no-repeat;
      background-size: contain;
}

#imgViewer .carousel-item > .img-wrap > .img:not(div) {
      display: block;
}

#imgViewer .slick-arrow {
      position: absolute;
      top: 50%;
      border: none;
      background: none;
      outline: 0;
      width: 60px;
      height: 90px;
      font-size: 0.5em;
      text-align: left;
      text-indent: -9999px;
      -webkit-transform: translateY(-50%);
                              transform: translateY(-50%);
      z-index: 10;
}

#imgViewer .slick-arrow:before, #imgViewer .slick-arrow:after {
      position: absolute;
      top: 50%;
      left: 50%;
      width: 30px;
      height: 1px;
      background-color: #666;
      content: “”;
}

@media screen and (min-width: 992px) {
      #imgViewer .slick-arrow {
            width: 80px;
            height: 120px;
      }
      #imgViewer .slick-arrow:before, #imgViewer .slick-arrow:after {
            width: 40px;
      }
}

#imgViewer .slick-arrow.slick-prev {
      left: 0;
}

#imgViewer .slick-arrow.slick-prev:before, #imgViewer .slick-arrow.slick-prev:after {
      -webkit-transform-origin: left center;
                              transform-origin: left center;
}

#imgViewer .slick-arrow.slick-prev:before {
      -webkit-transform: translate(-50%, -50%) rotate(45deg);
                              transform: translate(-50%, -50%) rotate(45deg);
}

#imgViewer .slick-arrow.slick-prev:after {
      -webkit-transform: translate(-50%, -50%) rotate(-45deg);
                              transform: translate(-50%, -50%) rotate(-45deg);
}

#imgViewer .slick-arrow.slick-next {
      right: 0;
}

#imgViewer .slick-arrow.slick-next:before, #imgViewer .slick-arrow.slick-next:after {
      -webkit-transform-origin: right center;
                              transform-origin: right center;
}

#imgViewer .slick-arrow.slick-next:before {
      -webkit-transform: translate(-50%, -50%) rotate(45deg);
                              transform: translate(-50%, -50%) rotate(45deg);
}

#imgViewer .slick-arrow.slick-next:after {
      -webkit-transform: translate(-50%, -50%) rotate(-45deg);
                              transform: translate(-50%, -50%) rotate(-45deg);
}

#imgViewer__main {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
}

#imgViewer__main > .carousel-item:first-child:not([class*=’slick’]) {
      z-index: 10;
}

#imgViewer__main > .carousel-item:not(:first-child):not([class*=’slick’]) {
      position: absolute !important;
      top: 0;
      left: 0;
      opacity: 0;
}

#imgViewer__thumbnail {
      margin: 4px -2px 0;
}

#imgViewer__thumbnail .carousel-item {
      padding-left: 2px;
      padding-right: 2px;
}

#imgViewer__thumbnail .carousel-item > .img-wrap {
      -webkit-transition: 0.3s;
      transition: 0.3s;
      cursor: pointer;
}

#imgViewer__thumbnail .carousel-item > .img-wrap:not(.active):not(:hover):not(:focus) {
      opacity: 0.4;
}

#imgViewer__thumbnail .carousel-item > .img-wrap > .img {
      background-color: #333;
}

@media screen and (min-width: 768px) {
      #imgViewer__thumbnail {
            margin: 14px -7px 0;
      }
      #imgViewer__thumbnail .carousel-item {
            padding-left: 7px;
            padding-right: 7px;
      }
}

#imgViewer__main > .carousel-item:first-child:not([class*=’slick’]) {
      z-index: 10;
}

#imgViewer__main > .carousel-item:not(:first-child):not([class*=’slick’]) {
      position: absolute !important;
      top: 0;
      left: 0;
      opacity: 0;
}

#imgViewer__thumbnail > .carousel-item {
      float: left;
      width: 33.3333333%;
}

@media screen and (min-width: 768px) {
      #imgViewer__thumbnail > .carousel-item {
            width: 25%;
      }
}

@media screen and (min-width: 768px) and (max-width: 991px) {
      #imgViewer__thumbnail > .carousel-item:nth-child(n+5) {
            display: none !important;
      }
}

@media screen and (min-width: 992px) {
      #imgViewer__thumbnail > .carousel-item {
            width: 16.6666666%;
      }
      #imgViewer__thumbnail > .carousel-item:nth-child(n+7) {
            display: none !important;
      }
}

@media screen and (max-width: 767px) {
      #imgViewer__thumbnail > .carousel-item:nth-child(n+4) {
            display: none !important;
      }
}

カテゴリ:未分類
↑pagetop