本文へスキップ
HELLO!
HIGASHIYUKI®.COM
0   /   100
ヒーロー

ポインタに合わせてヒーローの山並みの層がずれ、奥行きが出る

デモ

操作:カーソルを動かす。 「さわってみる」を押すと自動再生が止まり、マウスで動かせます。

仕様

用途
ヒーロー
動作
DOM
種類
レシピ
動作確認
three 0.186.0・gsap 3.15.0
公開日

しくみと調整

太陽と雲・遠くの山・見出し・中ほどの丘・手前の丘・前景の森を、SVG の層にして重ねます。各層は、カーソルの画面の中心からのずれに、層ごとの深さを掛けた分だけ、逆向きに動かします。手前の層ほど深さを大きくし、少し遅れてついてくるようにすると、奥行きが出ます。

見出しは山と丘のあいだの層に挟み、手前の丘で文字の下のほうを隠します。黒の幕(左 0.6 から右 0.3 へ薄くなる)は、見出しの層のすぐ奥に置いて動かさず、空・太陽・遠くの山だけを暗くします。見出しより手前にかけると、白い見出しまで灰色に沈みます。

層は画面より少し大きく作り、ずれても端が見えないようにします。文字を含む層には will-change を付けません。専用のレイヤーに載せると、文字の描かれ方が変わることがあります。

実際のサイトでは、pointermove でカーソルの位置を quickTo に渡し、カーソルが外れたら 0 に戻します。追従にかける時間は層ごとに変え、手前の層ほど遅れてついてきます。タッチ端末と、動きを減らす設定のときは、つなぎません。

層ごとの深さと遅れ、縦のずれの割合、画面の外へ広げる量、色で調整します。1 枚のカードを 3D で傾ける演出や、スクロールで列が流れる演出と違い、カーソルで多層の風景がずれます。旅行・アウトドア・ゲーム・キッズ向けのヒーローに向いています。

コード

HTML
<section class="hero" aria-labelledby="hero-title">
  <!-- 層は奥から手前の順に置く。data-depth が大きいほど手前で、よく動く(ポインタと逆へ) -->
  <img class="hero__layer" src="/img/hero-sky.jpg" alt="" data-depth="0.012" data-lag="0.08">
  <img class="hero__layer" src="/img/hero-far.png" alt="" data-depth="0.026" data-lag="0.1">

  <!-- 幕は見出しのすぐ奥。ここより手前に置くと、白い見出しまで灰色に沈む -->
  <span class="hero__scrim" aria-hidden="true"></span>

  <h1 class="hero__title" id="hero-title" data-depth="0.042" data-lag="0.12">WANDER</h1>

  <!-- 見出しより手前の層。下のほうを隠して、見出しが山と丘の間に挟まって見える -->
  <img class="hero__layer" src="/img/hero-mid.png" alt="" data-depth="0.064" data-lag="0.15">
  <img class="hero__layer" src="/img/hero-near.png" alt="" data-depth="0.092" data-lag="0.18">
  <img class="hero__layer" src="/img/hero-front.png" alt="" data-depth="0.13" data-lag="0.22">

  <p class="hero__copy">静かな山道、あたたかな山小屋、のんびりした朝。</p>
</section>
CSS
.hero {
  /* 層を画面の外へ広げる量。いちばん手前の層のずれ(depth × 画面の半分)より大きくする */
  --overscan: 8%;
  position: relative;
  height: 100svh;
  overflow: hidden;
  background: linear-gradient(180deg, #b9a8ff 0%, #e7b8f0 34%, #ffc9d4 58%, #ffe2c6 76%);
  color: #ffffff;
}

/* 画面より大きく置く。ずれても端が見えない */
.hero__layer {
  position: absolute;
  inset: calc(var(--overscan) * -1);
  object-fit: cover;
  pointer-events: none;
}

.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.3) 100%);
  pointer-events: none;
}

.hero__title {
  position: absolute;
  left: 0;
  right: 0;
  top: 27%;
  margin: 0;
  text-align: center;
  font-size: clamp(64px, 17vw, 216px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  /* 文字を含む層には will-change を付けない。専用のレイヤーに載せると、文字の描かれ方が変わる */
}

.hero__copy {
  position: absolute;
  left: 6%;
  bottom: 8%;
  max-width: 28em;
  margin: 0;
  font-size: 18px;
  line-height: 1.8;
  letter-spacing: 0.04em;
}
JavaScript
import { gsap } from "gsap";

const VERTICAL = 0.5; // 縦のずれを横に対してどれだけにするか
const canHover = window.matchMedia("(hover: hover) and (pointer: fine)").matches;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

// タッチ端末ではポインタが無く、動きを減らす設定の人には負担になるので、どちらでもつながない
if (canHover && !reduceMotion) {
  document.querySelectorAll(".hero").forEach((hero) => {
    const movers = [...hero.querySelectorAll("[data-depth]")].map((node) => {
      const depth = parseFloat(node.dataset.depth);
      const lag = parseFloat(node.dataset.lag) || 0;
      // 手前の層ほど遅れてついてくると、奥行きがはっきりする
      const duration = 0.5 + lag * 4;
      return {
        depth,
        x: gsap.quickTo(node, "x", { duration, ease: "power3.out" }),
        y: gsap.quickTo(node, "y", { duration, ease: "power3.out" }),
      };
    });

    hero.addEventListener("pointermove", (event) => {
      const rect = hero.getBoundingClientRect();
      const dx = event.clientX - (rect.left + rect.width / 2);
      const dy = event.clientY - (rect.top + rect.height / 2);
      for (const mover of movers) {
        // ポインタと逆へ動かす。中心からの距離に比例させるので、端ほど大きくずれる
        mover.x(-dx * mover.depth);
        mover.y(-dy * mover.depth * VERTICAL);
      }
    });

    hero.addEventListener("pointerleave", () => {
      for (const mover of movers) {
        mover.x(0);
        mover.y(0);
      }
    });
  });
}

インタラクションデザインについて、
お気軽にご相談ください。

ここに並べているのは、Web サイトの中で動く UI のサンプルです。新しく作るサイトに組み込むことも、いま公開中のサイトに、ボタンやヒーロー、スクロールの演出だけを足すこともできます。設計から実装までの進め方はWeb サイト制作、端末ごとの見え方や表示速度の考え方はレスポンシブウェブサイトのページにまとめています。映像として作る動きをお探しの場合はモーショングラフィックスをご覧ください。