JavaScriptアニメーションの現在地: タイムライン管理に強いライブラリ徹底比較
はじめに (対象読者・この記事でわかること)
本記事は、Webフロントエンドで「時間軸(タイムライン)」を明示的に制御しながら複雑なアニメーションを設計したいデベロッパーを対象としています。CSSトランジションや簡単なJSアニメーションから一歩進んで、複数要素・多段階・スクロール連動・再生コントロール(再生/一時停止/逆再生/シーク)を必要とする方向けに、ライブラリ選定の指針と実装の具体例を提供します。読了後は、GSAP、Anime.js、Motion One、Popmotionの特性を理解し、要件に応じた最適解を選べるようになります。
開発環境・前提知識
この記事で紹介する内容は、以下の環境で動作確認をしています。
| カテゴリ | バージョン/情報 |
|---|---|
| OS | macOS Sonoma 14.x |
| 言語/FW | Node.js 20.x / Vite 5 |
| ライブラリ | GSAP 3.12.x / Anime.js 3.2.x / Motion One 10.x |
また、この記事を読み進める上で、以下の知識があるとスムーズです。 * JavaScriptの基本文法、DOM操作、イベント処理 * CSSアニメーションの基礎(transform, opacity, easing) * モジュールバンドラ(Vite/Webpack)またはESMの利用
なぜ「タイムライン管理」が重要か:概要・背景
単発のフェードやスライドならCSSやrequestAnimationFrameで十分ですが、実務では「Aが終わったらBを開始」「CはBの途中0.3秒で差し込む」「スクロール量で全体を進行」など、アニメーション間の時間的関係を設計する必要が増えます。これを命令的なsetTimeoutやイベント連鎖で書くと、可読性・メンテナンス性が急激に低下します。
タイムライン対応ライブラリは、シーク可能な時間軸を提供し、区間ごとにキーフレームやイージングを定義できます。これにより、状態遷移の設計が「時系列の設計図」へと引き上がり、以下の利点が得られます。 - 複数要素の同期・遅延・重なりを宣言的に記述 - 進行度(0〜1、またはミリ秒)での制御(再生、逆再生、停止、ジャンプ) - スクロール、メディア再生、インタラクションなど外部信号とのマッピング - パフォーマンスチューニング(transform/opacity中心、will-change最適化、スロットリング)
代表的な選択肢として、GSAP、Anime.js、Motion One、Popmotion(Framer Motionの基盤)があります。本記事では特に、実務での完成度が高く、タイムライン設計に強いGSAPを軸に、Anime.jsやMotion Oneとの比較と実装手順を解説します。
実務で使えるタイムライン実装ガイド:ライブラリ比較と手順
ここが記事のメインパートです。GSAPを中心に、Anime.jsとMotion Oneを補完的に紹介し、タイムライン設計→コーディング→スクロール連動→デバッグの流れを示します。
ステップ1:ユースケース定義とライブラリ選定
要件を以下の観点で整理します。 - 同期/非同期の複雑さ: 多段階、並行進行、差し込みタイミングが多いほどタイムライン必須 - スクロール連動: ページ内のセクション分割、ピン留め、スクラブが必要か - 依存ライブラリ・ファイルサイズ: バンドル制約、モバイル回線を考慮 - フレームワーク統合: React/Vue/Svelteでの扱いやすさ、SSRの可否 - SVG/3D/モーションパス: 高度な表現の必要性
おすすめの初期判断: - 複雑/大規模/商用案件/スクロール連動: GSAP(ScrollTrigger/Timelineが強力) - 中規模/軽量志向/シンプルなタイムライン: Anime.js - 近代API/超軽量/WAAPI寄り: Motion One - Reactで物理挙動/宣言的UI統合: Framer Motion(タイムライン自体は弱め、遷移制御が主眼)
ステップ2:GSAPでの基本タイムライン
インストール: ```bash:command.sh npm i gsap
要素を段階的に登場させるタイムライン例:
```html:index.html
<div class="hero">
<h1 class="title">Hello Timeline</h1>
<p class="lead">時間軸で制御すると管理が楽になります。</p>
<button class="cta">Get Started</button>
</div>
```js:main.js import { gsap } from "gsap";
// 基本のタイムラインを作成(デフォルト継続時間やイージングを設定) const tl = gsap.timeline({ defaults: { duration: 0.6, ease: "power2.out" } });
// 0秒時点でタイトルをフェードイン&上から tl.from(".title", { y: 24, opacity: 0 });
// 0.2秒遅らせて段落を続ける tl.from(".lead", { y: 16, opacity: 0 }, "+=0.2");
// 前の開始から-0.1秒かぶせてボタンを拡大しつつ出現 tl.from(".cta", { scale: 0.8, opacity: 0 }, "-=0.1");
// コントロール例 document.querySelector(".cta").addEventListener("click", () => { if (tl.progress() < 1) tl.play(); else tl.reverse(); });
ポイント:
- "+="で相対遅延、"-="でオーバーラップを定義
- tl.progress(0.5)で中間へシーク、tl.pause()/play()/reverse()で操作
- defaultsで全体の表情を一括調整
### ステップ3:ScrollTriggerでスクロール連動
インストール/登録:
```js:scroll.js
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
セクションをスクロール進行度に同期: ```js:scroll.js const tl = gsap.timeline({ scrollTrigger: { trigger: ".section", start: "top center", end: "bottom top", scrub: true, // スクロール量に追従 pin: true, // セクションを固定(ピン留め) markers: false // デバッグマーカー(必要に応じてtrue) } });
tl.from(".feature-1", { x: -60, opacity: 0 }) .from(".feature-2", { x: 60, opacity: 0 }, "-=0.2") .from(".feature-3", { y: 40, opacity: 0 }, "-=0.1");
利点:
- スクラブとピンの組み合わせでインタラクティブなストーリーテリング
- タイムラインの再利用(スクロール動作を差し替えるだけで適用可能)
### ステップ4:アニメーション設計の実務Tips
パフォーマンス:
- transform/opacity中心にし、left/topやbox-shadowアニメは避ける
- will-change: transform; を必要箇所に限定し適用
- 画像・フォントのプリロード、IntersectionObserverで遅延初期化
設計:
- 時間で考えるより「イベント設計→時間へ投影」する(意図が明確)
- パーツごとにサブルーチン的なタイムラインを返す関数化が有効
- デザイナーと共通言語(秒・イージング・キーフレーム)を持つ
デバッグ:
- GSDevTools(有料)やScrollTriggerのmarkersで確認
- tl.totalDuration()を把握し、区間ごとにラベルを打つ
```js:labels.js
tl.addLabel("intro")
.from(".logo", { opacity: 0 })
.addLabel("cta")
.from(".button", { y: 20, opacity: 0 }, "cta");
tl.tweenFromTo("intro", "cta");
ステップ5:Anime.jsで軽量なタイムライン
インストール: ```bash:command.sh npm i animejs
基本タイムライン:
```js:anime.js
import anime from "animejs/lib/anime.es.js";
const tl = anime.timeline({
autoplay: false,
easing: "easeOutQuad",
duration: 600
});
tl.add({ targets: ".title", translateY: [-20, 0], opacity: [0, 1] })
.add({ targets: ".lead", translateY: [-12, 0], opacity: [0, 1] }, "+=150")
.add({ targets: ".cta", scale: [0.9, 1], opacity: [0, 1] }, "-=100");
document.querySelector(".cta").addEventListener("click", () => {
tl.reverse(!tl.reversed); // 反転切替
tl.play();
});
特徴: - 軽量で学習コスト低い - タイムラインAPIは十分実用的 - スクロール連動は外部実装(IntersectionObserverや自作スクラブ)で補完
簡易スクラブ例(0〜1を進行度に反映): ```js:scroll-scrub.js const timeline = anime.timeline({ autoplay: false, duration: 3000 }); timeline.add({ targets: ".box", translateX: [0, 400], opacity: [0, 1] }) .add({ targets: ".box", rotate: 45 }, "+=0");
const section = document.querySelector(".section"); function updateScrub() { const rect = section.getBoundingClientRect(); const h = window.innerHeight; const progress = 1 - Math.min(Math.max((rect.bottom) / (rect.height + h), 0), 1); timeline.seek(progress * timeline.duration); } document.addEventListener("scroll", updateScrub, { passive: true }); updateScrub();
### ステップ6:Motion One(WAAPI寄り・超軽量)
インストール:
```bash:command.sh
npm i motion
基本: ```js:motion.js import { animate, timeline } from "motion";
const sequence = [ [".title", { y: [-20, 0], opacity: [0, 1] }, { duration: 0.5 }], [".lead", { y: [-12, 0], opacity: [0, 1] }, { at: "+0.2" }], [".cta", { scale: [0.9, 1], opacity: [0, 1] }, { at: "-0.1" }] ];
const tl = timeline(sequence, { easing: "ease-out" });
// 再生制御(WAAPI準拠) tl.pause(); document.querySelector(".cta").addEventListener("mouseenter", () => tl.play());
特徴:
- Web Animations APIを活用、非常に小さい
- 配列ベースのタイムラインで直感的
- スクロール連動はIntersectionObserverや補助ライブラリで実装
### ステップ7:React/Vue統合の要点
React:
- GSAPはuseLayoutEffect内で初期化し、クリーンアップでkill
- refsでDOM要素を取得し、再レンダーで重複初期化を避ける
```jsx:Hero.jsx
import { useLayoutEffect, useRef } from "react";
import { gsap } from "gsap";
export default function Hero() {
const root = useRef(null);
useLayoutEffect(() => {
const ctx = gsap.context(() => {
const tl = gsap.timeline({ defaults: { duration: 0.6 } });
tl.from(".title", { y: 20, opacity: 0 })
.from(".lead", { y: 12, opacity: 0 }, "-=0.2");
}, root);
return () => ctx.revert();
}, []);
return (
<section ref={root}>
<h1 className="title">Hello</h1>
<p className="lead">React + GSAP</p>
</section>
);
}
Vue: - onMountedで初期化、onBeforeUnmountでkill - script setup + refでDOM参照
ハマった点やエラー解決
エラー内容:
Invalid property scrollTrigger set to [object Object]
解決策: - gsap.registerPlugin(ScrollTrigger) を呼んでいない。import後ただちに登録する。
エラー内容:
GSAP target not found. https://greensock.com
解決策: - セレクタの解決タイミングが早い。DOMが描画済みのフック(DOMContentLoaded、useLayoutEffect、onMounted)で初期化する。コンポーネントの条件分岐で非表示の場合も注意。
症状: - スクロールがカクつく/ドロップフレーム 解決策: - transform/opacityへ置き換え、画像を遅延ロード、will-changeは必要箇所のみ、ScrollTriggerのrefresh()を画像読み込み後に実行。
症状: - タイムラインが重複して加算される 解決策: - ページ遷移や再レンダー時にtl.kill()またはcontext().revert()でクリーンアップする。
まとめ
本記事では、タイムライン管理に強いJavaScriptアニメーションライブラリを比較し、GSAPを中心にAnime.jsとMotion Oneの実装手順と選定基準を解説しました。 - 複雑・商用・スクロール連動にはGSAPが堅実 - 軽量・シンプルな構成にはAnime.jsが扱いやすい - 近代API/超軽量志向ならMotion Oneが有力
読者は要件に応じて最適なライブラリを選び、時間軸ベースで保守しやすいアニメーションを設計できるはずです。次回は、パフォーマンス計測(FPS/CLS/LCP)やアクセシビリティ(reduced motion対応)、コンポーネント設計パターンをさらに深掘りします。
参考資料
- GSAP公式ドキュメント: https://gsap.com/docs/
- ScrollTriggerガイド: https://gsap.com/docs/v3/Plugins/ScrollTrigger/
- Anime.js: https://animejs.com/
- Motion One: https://motion.dev/
- Framer Motion: https://www.framer.com/motion/
- Web Animations API: https://developer.mozilla.org/docs/Web/API/Web_Animations_API