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

スクロールでカードが下からふわっと順に現れる

デモ

操作:スクロール。 「さわってみる」を押すと自動再生が止まり、マウスで動かせます。 触っている間は、ホイールでデモの中がスクロールします。

仕様

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

しくみと調整

カードを 48px 下げて透明にしておき、上端が画面の下から 15% の高さに届いたところで、元の位置へ戻しながら見せます。同じころに画面へ入ったカードはひとまとめに扱い、0.1 秒ずつずらして出すので、行ごとに左から順に現れます。

動く距離は 48px、時間は 0.8 秒、ease は power3.out です。距離を大きくすると、読み進める速さに動きが追いつかず、遅れて出てくるように見えます。ずらす間隔の 0.1 秒は、3 枚の行がおよそ 0.2 秒で出そろう長さで、これより広げると最後の 1 枚を待つ感じが出ます。

出し始める線(start)は、画面の下端ではなく少し内側に置きます。下端に置くと、カードが顔を出した瞬間に動き始めるので、いちばん動いている所が画面の外で終わってしまいます。

1 枚ずつ結ぶのではなく、まとめて扱うのが要点です。1 枚ずつだと、速くスクロールしたときに何枚もばらばらに動き出し、順に現れる感じが消えます。戻したときに消えてしまわないよう、出す向きだけを書いて、上へ戻したときは何もしません。

動きを減らす設定の人には、最初から出ている状態で見せます。記事の一覧・実績・商品の並びなど、同じ形の箱が続く節に向いています。

コード

HTML
<section class="cards" aria-labelledby="cards-title">
  <h2 class="cards__title" id="cards-title">Journal</h2>

  <div class="cards__grid">
    <!-- 同じ形の箱を並べるだけ。枚数が増えても、つなぎ方は変わらない -->
    <article class="card">
      <a class="card__link" href="/journal/quiet-interfaces/">
        <img class="card__image" src="/img/journal-01.jpg" alt="" width="800" height="500">
        <h3 class="card__name">Quiet interfaces</h3>
        <p class="card__text">目立たせるより、読みやすさを選んだ画面の話。</p>
      </a>
    </article>

    <article class="card">
      <a class="card__link" href="/journal/slow-motion/">
        <img class="card__image" src="/img/journal-02.jpg" alt="" width="800" height="500">
        <h3 class="card__name">Slow motion</h3>
        <p class="card__text">速さではなく、間の取り方で伝わる動きについて。</p>
      </a>
    </article>

    <article class="card">
      <a class="card__link" href="/journal/paper-and-ink/">
        <img class="card__image" src="/img/journal-03.jpg" alt="" width="800" height="500">
        <h3 class="card__name">Paper and ink</h3>
        <p class="card__text">紙の組み方を、画面の余白へ持ち込んだ記録です。</p>
      </a>
    </article>
  </div>
</section>
CSS
.cards {
  --ink: #262626;
  --mute: #6b6b6b;
  padding: 64px 6vw;
  background-color: #f2f1ed;
  color: var(--ink);
}

.cards__title {
  margin: 0 0 32px;
  font-size: clamp(24px, 2.6vw, 32px);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.cards__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 32px;
}

/* カードは白い面。角は 4px で、影は付けない */
.card {
  border-radius: 4px;
  background-color: #ffffff;
}

.card__link {
  display: block;
  padding: 16px;
  color: inherit;
  text-decoration: none;
}

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

.card__image {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

.card__name {
  margin: 16px 0 0;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.02em;
}

.card__text {
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 1.8;
  letter-spacing: 0.04em;
  color: var(--mute);
}
JavaScript
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

const cards = gsap.utils.toArray(".card");
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

// 動きを減らす設定では何もしない(カードは最初から出ている状態のまま)
if (cards.length > 0 && !reduceMotion) {
  gsap.set(cards, { y: 48, opacity: 0 });

  // 同じころに画面へ入ったカードをまとめて扱う。1 枚ずつ結ぶと、速く送ったときにばらばらに動き出す
  ScrollTrigger.batch(cards, {
    // 画面の下端ではなく、少し内側から出し始める。下端だと、いちばん動く所が画面の外で終わる
    start: "top 85%",
    onEnter: (batch) =>
      gsap.to(batch, { y: 0, opacity: 1, duration: 0.8, ease: "power3.out", stagger: 0.1, overwrite: true }),
  });
}

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

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