Motionのscroll()で作るパララックス入門

はじめに

スクロール連動の演出を入れたいとき、CSSだけで頑張ると調整が大変になりがちです。特に「カードごとに少しだけ動きや透明度を変える」ようなUIは、実装が増えるほど管理コストが上がります。

そんなときに扱いやすいのが Motion です。scroll() の進捗値を使えば、JavaScript側で演出ロジックをまとめて管理できます。必要なら animate() を組み合わせて、初期表示の演出も同じライブラリ内で完結できます。

この記事では、Motion の基本から入り、実務で使いやすい「パララックスカード」の実装までをまとめます。

機能やライブラリの概要

Motion は、React だけでなくバニラ JavaScript でも使えるアニメーションライブラリです。2024年11月に独立プロジェクトとして Motion に統合され、現在は motion.dev でドキュメントが管理されています。

今回使う中心機能は次の2つです。

  • animate(): 単発のアニメーションを簡潔に書ける
  • scroll(): スクロール進捗 (01) に値を連動できる

スクロール演出を「CSSアニメーションの寄せ集め」にしないで、ひとつのロジックとして整理できるのが実務で強いポイントです。

インストール方法

まずはパッケージを追加します。既存のNode環境があるなら次のどれかでOKです。

PowerShell
npm install motion
PowerShell
pnpm add motion
PowerShell
yarn add motion

読み込みは次の形です。

JavaScript

JavaScript
import { animate, scroll } from "motion"

基本の使い方

まずは animate() の最小構成です。カード一覧の初期表示にフェードアップを入れます。

See the Pen Motion scroll() by watanabe (@web-sourcecode) on CodePen.

HTML

HTML
<section class="news-list" id="js-news-list">
  <article class="news-card">記事カード A</article>
  <article class="news-card">記事カード B</article>
  <article class="news-card">記事カード C</article>
</section>

JavaScript

JavaScript
import { animate } from "motion"

const cards = document.querySelectorAll(".news-card")

animate(
  cards,
  { opacity: [0, 1], y: [16, 0] },
  { duration: 0.45, delay: (index) => index * 0.08, easing: "ease-out" }
)

この形だけでも、初期表示の読みやすさがかなり上がります。特にカードUIは、同時表示より少しだけ段差をつけたほうが視線誘導しやすいです。

便利な使いどころ

scroll() は次のような実装で効果を出しやすいです。

  • 記事カードのパララックス演出
  • 固定ヘッダーの縮小や透明度制御
  • セクション見出しの進捗インジケーター
  • 横スクロール領域の進捗可視化

共通点は「派手な演出」より「読ませるための微調整」です。scroll() はこの粒度のUI改善にちょうど良いです。

応用コード

ここではカードごとに係数を変え、奥行き感のあるパララックスを付けます。prefers-reduced-motion も入れて、動きを減らしたい環境では演出を弱めます。

See the Pen Motion scroll() 2 by watanabe (@web-sourcecode) on CodePen.

CSS

CSS
.news-list {
  display: grid;
  gap: 16px;
}

.news-card {
  padding: 20px;
  border-radius: 14px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  box-shadow: 0 12px 30px rgba(15, 23, 42, 0.07);
  will-change: transform, opacity;
}

JavaScript

JavaScript
import { animate, scroll } from "motion"

const cards = [...document.querySelectorAll(".news-card")]
const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches

animate(
  cards,
  { opacity: [0, 1], y: [16, 0] },
  { duration: 0.45, delay: (index) => index * 0.08, easing: "ease-out" }
)

scroll((progress) => {
  const p = Math.min(Math.max(progress, 0), 1)

  cards.forEach((card, index) => {
    const intensity = prefersReduced ? 0.2 : 1
    const depth = (index + 1) * 8 * intensity
    const translateY = (0.5 - p) * depth
    const opacity = 1 - Math.abs(0.5 - p) * 0.18 * intensity

    card.style.transform = `translateY(${translateY}px)`
    card.style.opacity = String(Math.max(0.82, opacity))
  })
})

この実装だと、スクロール位置の調整は depthopacity の係数を触るだけで済みます。デザイン調整の会話がしやすく、あとから保守する人にも優しい構成です。

注意点

  • 1画面で動く要素を増やしすぎない
    パララックスは効かせすぎると可読性が落ちます。まずは主要カードだけに限定するのが安全です。
  • will-change の乱用を避ける
    有効な場面は多いですが、全要素に付けると逆効果になることがあります。動かす要素だけに絞ります。
  • 減速設定を最初に入れる
    prefers-reduced-motion を最初から入れておくと、アクセシビリティ対応の後戻りが減ります。

まとめ

Motion の scroll() は、JavaScript主導でスクロール演出を整理したいときにとても相性が良いです。CSSだけで分散しがちなロジックを1か所に寄せられるので、実務運用がかなり楽になります。

まずはカードやヘッダーのような小さな領域から導入し、効果が確認できたら他セクションへ広げる進め方がおすすめです。

ポイント

  • scroll() の進捗値で演出を一元管理すると保守しやすい
  • カードUIは「少しだけ動かす」設計のほうが読みやすい
  • prefers-reduced-motion を同時実装すると運用が安定する

参考リンク

read next