Swiperで作る“寄り→引き”ヒーロースライダー実装ガイド 〜ズーム方向指定・Ken Burns効果・進捗ラインまで〜

Webサイトのファーストビューでは、「ただ画像が切り替わるだけ」ではなく、
写真そのものが語りかけてくるような演出が求められます。

最近よく見かけるのが、

  • 写真がゆっくり 寄り→引き しながら表示される
  • スライド切り替え前に 下の線が伸びていく
  • 右・左・右下など ズームの方向がスライドごとに違う

といった、ホテル・ブライダル系サイトで使われる上品なヒーロースライダーです。

この記事では、Swiperを使ってこの演出を安全かつ自然に実装する方法を解説します。

なぜ「寄り→引き」が効果的なのか?

一般的なKen Burnsエフェクトは「ズームイン(寄り)」ですが、

  • 最初から寄りすぎて見える
  • 切り替え直後に圧迫感が出る

という弱点があります。

そこでおすすめなのが、

表示直後は少し寄っていて、時間とともに引いていく

という「寄り → 引き」構成です。

これにより、

  • 初見でインパクトを出せる
  • 時間経過で余白が生まれ、次のスライドへの期待感が出る

という効果が得られます。

実装の全体構成

今回のスライダーは、以下の3要素で構成します。

  1. Swiperのフェードスライダー
  2. CSSで制御するKen Burns(寄り→引き)
  3. 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回だけ

この構成なら、ブライダル・ホテル・高級ブランド系のヒーロー演出
安心して量産できます。