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

文字化けしながら単語が切り替わる

デモ

仕様

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

しくみと調整

単語が崩れながら、左の字から順に次の単語へ確定していきます。確定した字数はイージングで増やし、まだ確定していない位置は毎フレーム乱数で字を選び直しています。和文のかなと漢字は全角でどれも同じ幅なので、崩れに使う字も全角のかな・漢字・記号だけにそろえ、字詰めをfont-kerningで切っています。こうすると崩れている字が入れ替わっても語の幅が変わらず、字だけが入れ替わって見えます。

語は1.1秒読ませてから0.7秒かけて崩れます。イージングはpower2.inOutで、序盤はゆっくり確定し、終盤で一気に決まります。

崩れ続ける文字をそのまま読み上げに渡すと、変わるたびに読み上げが割り込んでしまいます。読み上げには単語の一覧をまとめた固定の文言を渡し、崩れて見える文字そのものは読み上げから外しています。5秒より長く動き続けるので、止める操作も添えています。

つまみは読ませる時間・崩れる時間・崩れに使う字の種類です。種類を絞るほど、規則性のある崩れ方に見えます。ロゴのそばの一言や、プロダクト名を並べて見せたい所に向いています。

コード

HTML
<div class="word-scramble">
  <p class="word-scramble__label" aria-hidden="true">ブランド</p>
  <p class="word-scramble__sr">ブランド、デザイン、動画制作、運用支援。</p>
  <button class="word-scramble__toggle" type="button" aria-pressed="false">一時停止</button>
</div>

CSS
.word-scramble {
  display: grid;
  justify-items: center;
  gap: 24px;
  padding: 96px 6vw;
  background: #f2f1ed;
  color: #262626;
  text-align: center;
}

/* 和文の全角の字はどれも同じ幅。字詰め(kern)だけ切ると、字が入れ替わっても語の幅が変わらない */
.word-scramble__label {
  margin: 0;
  font-size: clamp(32px, 7vw, 64px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: 0.08em;
  font-kerning: none;
}

/* 読み上げにだけ届く固定の文言。画面には出さない */
.word-scramble__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.word-scramble__toggle {
  height: 40px;
  padding: 0 20px;
  border: 0;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px #262626;
  background: transparent;
  color: #262626;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.word-scramble__toggle[aria-pressed="true"] { background: #262626; color: #f2f1ed; }
.word-scramble__toggle:focus-visible { outline: 2px solid #262626; outline-offset: 4px; }

JavaScript
import { gsap } from "gsap";

const WORDS = ["ブランド", "デザイン", "動画制作", "運用支援"];
// 崩れに使う字は全角だけにする。半角を混ぜると、字が入れ替わるたびに語の幅が揺れる
const GLYPHS = "アイウカキクサシスタチツナニヌハヒフマミムヤユヨラリルワヲン日月火水木金土山川空色形線点面#%&*+=?";
const HOLD = 1.1; // 単語を読ませる時間
const SCRAMBLE = 0.7; // 崩れて次の単語になるまでの時間
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

document.querySelectorAll(".word-scramble").forEach((root) => {
  const label = root.querySelector(".word-scramble__label");
  const toggle = root.querySelector(".word-scramble__toggle");
  const state = { p: 0 };

  const tl = gsap.timeline({ repeat: -1 });
  WORDS.forEach((word, i) => {
    const next = WORDS[(i + 1) % WORDS.length];
    tl.call(() => (label.textContent = word))
      .to({}, { duration: HOLD })
      .to(state, {
        p: 1,
        duration: SCRAMBLE,
        ease: "power2.inOut",
        onUpdate: () => {
          // 確定した字数はイージングで進める。未確定の位置は毎フレーム字を選び直す
          const settled = Math.floor(state.p * next.length);
          let text = "";
          for (let c = 0; c < next.length; c++) text += c < settled ? next[c] : GLYPHS[(Math.random() * GLYPHS.length) | 0];
          label.textContent = text;
        },
        onComplete: () => (state.p = 0),
      });
  });

  // 動きを減らす設定では止めず、崩れをほぼ一瞬にする(読ませる間だけは残す)
  if (reduceMotion) tl.timeScale(20);

  toggle.addEventListener("click", () => {
    const paused = !tl.paused();
    tl.paused(paused);
    toggle.setAttribute("aria-pressed", String(paused));
  });
});

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

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