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

見出しの文字が波のように上下する

デモ

仕様

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

しくみと調整

見出しを 1 文字ずつに分け、上下に往復する動きを stagger で少しずつずらして重ねます。1 文字の動き自体は単純な上下運動で、ずれが積み重なることで波のように連なって見えます。

1 文字が下から上まで動くのに 0.792 秒、隣の文字とは 0.1188 秒ずつずらしています。振れ幅は文字の高さの 14% で、これより大きくすると波が誇張されすぎて読みにくくなります。

文字ごとに往復と繰り返しをかけるので、repeat と yoyo は stagger の中に書きます。外側に書くと、文字全体がまとめて往復してしまい、波になりません。

動き続けるトゥイーンの paused() を反転させるだけで、途中の位置から自然に止まったり動き出したりします。5 秒を超えて動き続けるものには、この形で止める手段を用意しておきます。

ヒーローの見出しや、楽しげなキャンペーンページに向いています。文字の太さが波打つ動きと違い、こちらは文字の位置そのものが上下します。

コード

HTML
<section class="wave-letters">
  <h2 class="wave-letters__heading">ゆらゆら、波に乗って</h2>
  <button class="wave-letters__toggle" type="button" aria-pressed="false" hidden>一時停止</button>
</section>

CSS
.wave-letters {
  --bg: #111111;
  --ink: #eeece6;
  display: grid;
  place-items: center;
  gap: 32px;
  min-height: 100vh;
  padding: 96px 6vw;
  background: var(--bg);
  color: var(--ink);
  text-align: center;
}

.wave-letters__heading {
  margin: 0;
  max-width: 16em;
  font-size: clamp(28px, 8vw, 112px); /* 和文 10 字が幅 375px の画面でも 1 行に収まる大きさ */
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: -0.02em;
}

.wave-letters__toggle {
  height: 40px;
  padding: 0 20px;
  border: 0;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--ink);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.wave-letters__toggle[aria-pressed="true"] {
  background: var(--ink);
  color: var(--bg);
}

.wave-letters__toggle:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

JavaScript
import { gsap } from "gsap";
import { SplitText } from "gsap/SplitText";

gsap.registerPlugin(SplitText);

const LIFT = 14; // 振れ幅(文字の高さに対する %。上下に同じだけ)
const HALF = 0.792; // 下から上まで(半周期)
const EACH = 0.1188; // 隣の文字とのずれ。小さいほど波長が長い
const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;

// 字形が決まってから分ける。仮の書体の幅で分けると、書体が差し替わったときに字がずれる
document.fonts.ready.then(() => {
  document.querySelectorAll(".wave-letters").forEach((section) => {
    const heading = section.querySelector(".wave-letters__heading");
    const toggle = section.querySelector(".wave-letters__toggle");
    const split = SplitText.create(heading, { type: "words,chars" });

    // 動きを減らす設定では静止したまま置く(止める手段も出さない)
    if (reduceMotion) return;

    const wave = gsap.fromTo(
      split.chars,
      { yPercent: LIFT },
      { yPercent: -LIFT, duration: HALF, ease: "sine.inOut", stagger: { each: EACH, repeat: -1, yoyo: true } }
    );

    // 5 秒より長く動き続けるので、止める手段を置く
    toggle.hidden = false;
    toggle.addEventListener("click", () => {
      const paused = !wave.paused();
      wave.paused(paused);
      toggle.setAttribute("aria-pressed", String(paused));
    });
  });
});

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

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