Swiper練習ページ
カードタイプの写真などをスライダーで動的に表現する実装は多いと思います。僕はライブラリーでSwiperを使います。練習用(デモ用)ページを作りました。
※東京ホームページ(BRISK様)を参考にしています。
https://b-risk.jp/blog/2022/04/swiper
※画像:ぱくたそ様のサイトから利用
https://www.pakutaso.com/
Card1
https://kslifehack.com/slider/#card1
標準的なスライダー
ホバーするとカードが少し上に動きます。
アクティブなpagenationは拡大します。
■html
<div class="swiper-area card01">
<div class="swiper p-slider js-slider ">
<div class="swiper-wrapper">
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
■JavaScript
const spotsSwiperWrap = document.querySelector(".card01 .swiper-wrapper");
const spotsSwiper = new Swiper(".card01 .js-slider", {
loop: true,
slidesPerView: 1,
spaceBetween: 16,
centeredSlides: true,
keyboard: true,
breakpoints: {
768: {
slidesPerView: 3,
spaceBetween: 30,
centeredSlides: false, // ← 中央寄せをやめる
initialSlide: 0, // ← 1枚目からスタート(デフォルトでもOK)
},
},
pagination: {
el: '.card01 .swiper-pagination',
clickable: true,
},
navigation: {
prevEl: ".card01 .swiper-button-prev",
nextEl: ".card01 .swiper-button-next",
},
});
Card2
https://kslifehack.com/slider/#card2
自動ループします。
中央のアクティブなスライドが拡大します。
両サイドのスライドは薄くしています。
■html
<div class="swiper-area card02">
<div class="swiper p-slider js-slider ">
<div class="swiper-wrapper">
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
■JavaScript
const spotsSwiperWrap02 = document.querySelector(".card02 .swiper-wrapper");
const spotsSwiper02 = new Swiper(".card02 .js-slider", {
loop: true,
slidesPerView: 1,
// スライドが6枚は必要 //
loopAdditionalSlides: 2,
spaceBetween: 16,
centeredSlides: true,
keyboard: true,
watchSlidesProgress: true,
speed: 1000,
autoplay: {
delay: 4000,
disableOnInteraction: false,
},
breakpoints: {
768: {
slidesPerView: 3,
spaceBetween: 50,
},
},
pagination: {
el: '.card02 .swiper-pagination',
clickable: true,
},
navigation: {
prevEl: ".card02 .swiper-button-prev",
nextEl: ".card02 .swiper-button-next",
},
});
spotsSwiper02.on('progress', function () {
spotsSwiper02.slides.forEach((slide) => {
const progress = slide.progress;
const opacity = 1 - Math.abs(progress * 0.5); // 中央=1, 両端=0.5〜0.3くらい
slide.style.opacity = opacity < 0.3 ? 0.3 : opacity; // 最低0.3に抑える
});
});
spotsSwiper02.on('setTransition', function (speed) {
spotsSwiper02.slides.forEach((slide) => {
slide.style.transition = `opacity ${speed}ms`;
});
});
Card3
https://kslifehack.com/slider/#card3
中央のアクティブスライドを拡大します。
「次へ」、「前へ」のボタンを疑似要素で任意のアイコンに変更し位置をアクティブスライド内に配置しています。
■html
<div class="swiper-area card03">
<div class="swiper p-slider js-slider ">
<div class="swiper-wrapper">
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_01.png" media="(min-width: 768px)">
<img src="./img/sp/woman_01.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_02.png" media="(min-width: 768px)">
<img src="./img/sp/woman_02.png" alt="">
</picture>
</div>
</div>
<div class="swiper-slide">
<div class="slide-img">
<picture>
<source srcset="./img/top/woman_03.png" media="(min-width: 768px)">
<img src="./img/sp/woman_03.png" alt="">
</picture>
</div>
</div>
</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
</div>
■css
.card03 .slide-img {
border-radius: 20px;
overflow: hidden; /* 画像がはみ出さないように */
img {
width: 100%;
height: auto;
transition: transform 0.4s;
transform-origin: center center; /* ←拡大の基準点を中央に */
object-fit: cover; /* 画像のアスペクト比を保ちながら、要素のサイズに合わせる */
}
}
.card03 {
margin-bottom: 200px;
padding-top: 100px;
.swiper {
overflow: visible; // ← 見切れないように
}
.swiper-slide {
width: 250px;
@media screen and (min-width:768px) {
}
}
.swiper-slide-active {
width: 250px;
height: auto;
position: relative;
}
.swiper-button-prev,
.swiper-button-next {
z-index: 100;
&::after {
position: absolute;
content:"";
width: 50px;
height: 50px;
background-size: contain;
top: 50%;
transform: translateY(-50%);
}
}
.swiper-button-prev::after {
background-image: url("../img/top/i-arrow-left-purple.png");
}
.swiper-button-next::after {
background-image: url("../img/top/i-arrow-right-purple.png");
}
}
/* ただし .card03 には無効化 */
.card03 .slide-img:hover {
transform: none !important;
transition: none !important;
}
■JavaScript
const SwiperWrap03 = document.querySelector(".card03 .swiper-wrapper");
const Swiper03 = new Swiper(".card03 .js-slider", {
loop: true,
loopAdditionalSlides: 2,
slidesPerView:'auto',
spaceBetween: 32,
centeredSlides: true,
autoHeight: true, // 高さを自動調整
initialSlide: 1, // ← これも必要かも(最初に中央に表示する用)
breakpoints: {
768: {
spaceBetween: 32,
},
0: {
spaceBetween: 16, // 0〜767px(SP)で16px
}
},
speed: 1000,
autoplay: {
delay: 4000,
disableOnInteraction: false,
},
navigation: {
prevEl: ".card03 .swiper-button-prev",
nextEl: ".card03 .swiper-button-next",
},
// init イベントで初期配置
on: {
init(swiper) {
moveNavigationButtons(swiper);
updateSlideScale(swiper); // スケール更新を追加
},
slideChange(swiper) {
moveNavigationButtons(swiper);
updateSlideScale(swiper); // スケール更新を追加
},
// トランジション中もスムーズに更新
setTransition(swiper, transition) {
swiper.slides.forEach(slide => {
slide.style.transition = `transform ${transition}ms`;
});
}
}
});
function moveNavigationButtons(swiper) {
const activeSlide = swiper.slides[swiper.activeIndex];
const prevButton = document.querySelector(".card03 .swiper-button-prev");
const nextButton = document.querySelector(".card03 .swiper-button-next");
const isSP = window.innerWidth <= 768; // SPサイズの判定
if (!activeSlide || !prevButton || !nextButton) return;
// アクティブスライドやボタンが存在しない場合は何もしない
prevButton.parentNode?.removeChild(prevButton);
nextButton.parentNode?.removeChild(nextButton);
activeSlide.appendChild(prevButton);
activeSlide.appendChild(nextButton);
// ボタンの位置を調整
if (isSP) {
prevButton.style.left = '-10px';
nextButton.style.right = '-10px';
} else {
prevButton.style.left = '-20px';
nextButton.style.right = '-20px';
}
}
function updateSlideScale(swiper) {
const isSP = window.innerWidth <= 768; // SPサイズの判定
const activeSlideScale = isSP ? '1.3' : '1.5';
const leftTranslate = isSP ? '-43px' : '-60px';
const rightTranslate = isSP ? '43px' : '60px';
swiper.slides.forEach((slide, index) => {
if (index === swiper.activeIndex) {
// アクティブスライド
const transform = `scale(${activeSlideScale}) translateX(0)`;
slide.style.transform = transform;
slide.style.zIndex = '10';
slide.style.opacity = '1';
} else if (index < swiper.activeIndex) {
// 左のスライド
const transform = `scale(1.0) translateX(${leftTranslate})`;
slide.style.transform = transform;
slide.style.zIndex = '1';
slide.style.opacity = '1';
} else {
// 右のスライド
const transform = `scale(1.0) translateX(${rightTranslate})`;
slide.style.transform = transform;
slide.style.zIndex = '1';
slide.style.opacity = '1';
}
});
}