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

ヒーロー

グラデーションの光の筋が見出しの文字を横切る

デモ

仕様

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

しくみと調整

ラベンダーからローズカラー、グレージュへと色の移る光の筋が、見出しの上を左から右へゆっくり横切っていきます。筋の色は、明るい文字の色より少しだけ暗くしました。光らせるというより、色のつやが文字の表面をなでていくように見せたかったからです。

仕組みは background-clip の text 指定で、背景を文字の形に切り抜いています。背景は「文字の色・光の筋・文字の色」を並べた1枚のグラデーションで、幅を文字幅の2倍に筋の幅を足した広さにしてあり、background-position を100%から0%へ動かすと、筋だけが文字の上を通り抜けます。両端はふだんの文字の色なので、通り過ぎれば元の見出しに戻り、繰り返しても継ぎ目は見えません。筋の色を増やしても幅を変えても、両端さえ文字の色にしておけば、このつながりは崩れません。

動きは power1.inOut で、通り始めと抜ける間際をゆっくりにしています。注意がいるのは字形のはみ出しで、背景の外に出た部分は塗られずに消えてしまいます。見出しには余白をとって字形を背景の内側に収め、欧文なら p や y のように下へ伸びる部分のぶん、下の余白を多めにとってください。文字そのものは変わらず動きも控えめなので、ヒーローの見出しや新機能の告知、価格の強調のように、何度も目に入る場所で繰り返し流しても邪魔になりません。

コード

HTML
<section class="shine">
  <h1 class="shine__heading">ひとつずつ、<br>磨きあげる。</h1>
</section>

CSS
.shine {
  --bg: #111111;
  --ink: #eeece6;
  --band: #b0a5ca, #d29b97, #d2b999; /* 光の筋の色(左から。ラベンダー・ローズカラー・グレージュ) */
  display: grid;
  place-items: center;
  min-height: min(70vh, 520px);
  padding: 96px 6vw;
  background: var(--bg);
  color: var(--ink);
  text-align: center;
}

.shine__heading {
  display: inline-block;
  margin: 0;
  /* 背景の外に出た字形は塗られないので、余白で内側に収める(欧文なら p・y の下がりの分) */
  padding: 0.06em 0.06em 0.14em;
  font-size: clamp(40px, 8vw, 96px);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: 0.02em;
}

JavaScript
import { gsap } from "gsap";

const BAND_WIDTH = 0.4; // 光の筋の幅(文字幅に対する割合)
const SWEEP = 1.8;
const PAUSE = 1.2; // 光の筋が通り過ぎてから、次に通るまでの間
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

document.querySelectorAll(".shine__heading").forEach((heading) => {
  if (reduceMotion) return; // 動きを減らす設定では、元の文字色のまま止めておく

  const css = getComputedStyle(heading.closest(".shine"));
  const base = css.getPropertyValue("--ink").trim();
  const BAND = css.getPropertyValue("--band").split(",").map((c) => c.trim()); // 光の筋の色は CSS の --band に並べる
  // 背景は「元の文字色・光の筋・元の文字色」を並べた 1 枚のグラデーション。文字幅の 2 倍 + 光の筋の幅に広げておく
  const total = 2 + BAND_WIDTH;
  const from = 100 / total;
  const to = ((1 + BAND_WIDTH) * 100) / total;
  const stops = BAND.map((color, i) => `${color} ${(from + ((to - from) * (i + 1)) / (BAND.length + 1)).toFixed(2)}%`);
  const image = `linear-gradient(90deg, ${base} ${from.toFixed(2)}%, ${stops.join(", ")}, ${base} ${to.toFixed(2)}%)`;

  heading.style.backgroundImage = image;
  heading.style.backgroundSize = `${(total * 100).toFixed(2)}% 100%`;
  heading.style.backgroundRepeat = "no-repeat";
  heading.style.backgroundPosition = "100% 0%";
  heading.style.webkitBackgroundClip = "text";
  heading.style.backgroundClip = "text";
  heading.style.color = "transparent";

  // background-position を 100% → 0% に動かすと、光の筋だけが文字の上を通る。両端は元の文字色だけなので継ぎ目が出ない
  gsap.to(heading, { backgroundPosition: "0% 0%", duration: SWEEP, ease: "power1.inOut", repeat: -1, repeatDelay: PAUSE });
});

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

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