Swiperで作る“寄り→引き”ヒーロースライダー実装ガイド 〜ズーム方向指定・Ken Burns効果・進捗ラインまで〜
Webサイトのファーストビューでは、「ただ画像が切り替わるだけ」ではなく、
写真そのものが語りかけてくるような演出が求められます。
最近よく見かけるのが、
- 写真がゆっくり 寄り→引き しながら表示される
- スライド切り替え前に 下の線が伸びていく
- 右・左・右下など ズームの方向がスライドごとに違う
といった、ホテル・ブライダル系サイトで使われる上品なヒーロースライダーです。
この記事では、Swiperを使ってこの演出を安全かつ自然に実装する方法を解説します。
なぜ「寄り→引き」が効果的なのか?
一般的なKen Burnsエフェクトは「ズームイン(寄り)」ですが、
- 最初から寄りすぎて見える
- 切り替え直後に圧迫感が出る
という弱点があります。
そこでおすすめなのが、
表示直後は少し寄っていて、時間とともに引いていく
という「寄り → 引き」構成です。
これにより、
- 初見でインパクトを出せる
- 時間経過で余白が生まれ、次のスライドへの期待感が出る
という効果が得られます。
実装の全体構成
今回のスライダーは、以下の3要素で構成します。
- Swiperのフェードスライダー
- CSSで制御するKen Burns(寄り→引き)
- autoplayと同期した下部の進捗ライン
重要なのは、すべてを同じ時間軸(autoplay delay)で同期させることです。
HTML(sample構造・簡略版)
※ class名・構造は サンプル用です。
<section class="sample-hero">
<div class="swiper sample-slider">
<div class="swiper-wrapper">
<div class="swiper-slide" data-origin="100% 100%">
<img src="sample01.jpg" alt="">
</div>
<div class="swiper-slide" data-origin="0% 50%">
<img src="sample02.jpg" alt="">
</div>
<div class="swiper-slide" data-origin="50% 0%">
<img src="sample03.jpg" alt="">
</div>
</div>
<div class="sample-progress">
<span class="sample-progress__bar"></span>
</div>
</div>
</section>
ポイント
data-originで ズーム方向を指定- JS側でこの値をCSS変数に反映させる
- HTMLは極力シンプルに保つ
CSS:寄り→引きのKen Burnsエフェクト
.sample-slider {
--kb-from: 1.10; /* 開始:寄り */
--kb-to: 1.02; /* 終了:引き */
--kb-time: 4.5s;
}
.sample-slider img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: var(--kb-origin, 50% 50%);
transform-origin: var(--kb-origin, 50% 50%);
transform: scale(var(--kb-from));
will-change: transform;
}
.sample-slider .is-active img {
animation: kbZoom var(--kb-time) linear both;
}
@keyframes kbZoom {
from { transform: scale(var(--kb-from)); }
to { transform: scale(var(--kb-to)); }
}
なぜ translate を使わないのか?
- translate を入れると 見切れ・ガタつきが発生しやすい
transform-originだけで方向演出は十分可能
ズーム方向の指定ルール
data-origin の値で直感的に制御できます。
| 演出 | 値 |
|---|---|
| 中央 | 50% 50% |
| 左 | 0% 50% |
| 右 | 100% 50% |
| 上 | 50% 0% |
| 下 | 50% 100% |
| 右下 | 100% 100% |
| 左上 | 0% 0% |
スライドごとに自由に設定できるのが最大のメリットです。
下に伸びる進捗ライン(autoplay同期)
.sample-progress {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background: rgba(255,255,255,.2);
}
.sample-progress__bar {
width: 0%;
height: 100%;
background: #fff;
transform-origin: left;
}
JavaScript(Swiper初期化・統合版)
const DURATION = 4500;
const bar = document.querySelector('.sample-progress__bar');
const slider = new Swiper('.sample-slider', {
loop: true,
effect: 'fade',
speed: 1200,
autoplay: {
delay: DURATION,
disableOnInteraction: false,
},
on: {
init(swiper) {
swiper.el.style.setProperty('--kb-time', `${DURATION / 1000}s`);
swiper.slides.forEach(slide => {
slide.style.setProperty(
'--kb-origin',
slide.dataset.origin || '50% 50%'
);
});
playZoom(swiper);
startBar();
},
slideChangeTransitionStart() {
resetBar();
},
slideChangeTransitionEnd(swiper) {
playZoom(swiper);
startBar();
}
}
});
function playZoom(swiper) {
swiper.slides.forEach(s => s.classList.remove('is-active'));
const active = swiper.el.querySelectorAll(
'.swiper-slide-active, .swiper-slide-duplicate-active'
);
active.forEach(slide => {
const img = slide.querySelector('img');
if (img) {
img.style.animation = 'none';
img.offsetHeight;
img.style.animation = '';
}
slide.classList.add('is-active');
});
}
function startBar() {
bar.style.transition = 'none';
bar.style.width = '0%';
requestAnimationFrame(() => {
bar.style.transition = `width ${DURATION}ms linear`;
bar.style.width = '100%';
});
}
function resetBar() {
bar.style.transition = 'none';
bar.style.width = '0%';
}
実装でハマりやすい注意点
Swiperを複数回初期化しない
- Ken Burns用
- 進捗ライン用
で Swiperを2回作るのはNG。
必ず 1インスタンスに統合します。
切り替え時にoriginを変更しない
- 表示後に
transform-originを変えると - 画像が「パッ」と動いて見える
初期化時に全スライドへ固定が正解。
まとめ
- 「寄り→引き」は scale値を逆にするだけ
- 方向指定は
data-originで安全に実装 - translateを使わず、origin制御が安定
- 進捗ラインは autoplay と完全同期させる
- Swiper初期化は 必ず1回だけ
この構成なら、ブライダル・ホテル・高級ブランド系のヒーロー演出を
安心して量産できます。


横スライドしながら拡大するスライド演出の作り方〜ズームインで魅せる、上質なキービジュアル表現〜
12月 24, 2025今日の日付と現在時刻を自動表示!「10月15日10:15 現在」をJavaScriptで実装する方法
10月 15, 2025SimpleBar.jsでスクロールバーを美しく!【実装レシピ&サンプル付き】
9月 25, 2025