要素アニメーション
個々のDOM要素にアニメーションを適用
SSGOI
from0
to1
spring
トランジションの例
様々なトランジション効果の使用方法:
import { transition } from '@ssgoi/react';
import { fade, scale, blur, slide, fly, rotate, bounce } from '@ssgoi/react/transitions';
/**
* [React] Auto Key Plugin
*
* プラグイン使用時、keyを省略できます。
* ファイル:行:列に基づいて一意のkeyが自動生成されます。
*
* Next.js 設定:
* ```ts
* // next.config.ts
* import SsgoiAutoKey from "@ssgoi/react/unplugin/webpack";
*
* const nextConfig = {
* webpack: (config) => {
* config.plugins.push(SsgoiAutoKey());
* return config;
* },
* };
* ```
*
* ⚠️ 注意: .map()リストではJSX keyだけで十分 - プラグインが自動処理
*
* // .map()リストではJSX keyだけで十分
* // プラグインが自動的にfile:line:col:${jsxKey}を生成
* {items.map((item) => (
* <div
* key={item.id} // JSX keyだけで十分
* ref={transition(fade())}
* >
* {item.name}
* </div>
* ))}
*
* プラグインなしの場合、keyは必須です。
*/
// Fadeトランジション
<div ref={transition(fade({ physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Fade効果
</div>
// Scaleトランジション
<div ref={transition(scale({ physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Scale効果
</div>
// Blurトランジション
<div ref={transition(blur({ amount: 10, physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Blur効果
</div>
// Slideトランジション(方向指定)
<div ref={transition(slide({ direction: 'left', physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Slide効果
</div>
// Flyトランジション(カスタム位置)
<div ref={transition(fly({ x: 200, y: -50, physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Fly効果
</div>
// Rotateトランジション
<div ref={transition(rotate({ physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Rotate効果
</div>
// Bounceトランジション
<div ref={transition(bounce({ physics: { spring: { stiffness: 300, damping: 30 } } }))}>
Bounce効果
</div>
// 明示的なkey(プラグインなしの場合は必須)
<div ref={transition({
key: "explicit-key",
...fade({ physics: { spring: { stiffness: 300, damping: 30 } } })
})}>
明示的なkeyを使用
</div>
基本構造
TransitionConfigインターフェース
interface TransitionConfig {
physics?: {
// Spring: スプリング物理 (ease-out効果、デフォルト)
spring?: {
stiffness: number; // スプリング剛性 (デフォルト: 300)
damping: number; // 減衰係数 (デフォルト: 30)
doubleSpring?: boolean | number; // ダブルスプリング効果 (デフォルト: false)
};
// Inertia: 慣性物理 (ease-in効果)
inertia?: {
acceleration: number; // 加速度 (デフォルト: 500)
resistance: number; // 抵抗係数 (デフォルト: 10)
};
// Custom Integrator: カスタム物理エンジン
integrator?: () => Integrator;
};
// 2つのアニメーションモードから1つを選択(両方は使用不可)
tick?: (progress: number) => void; // RAFベースのアニメーション
css?: (progress: number) => StyleObject; // Web Animation APIベース(推奨)
prepare?: (element: HTMLElement) => void; // アニメーション開始前の初期設定
onStart?: () => void;
onEnd?: () => void;
}
アニメーションモード: css vs tick
SSGOIは2つのアニメーションモードをサポートしています:
物理エンジンオプション
SSGOIは様々な物理エンジンをサポートし、自然なアニメーションを実現します:
Spring (スプリング物理)
ease-out効果を提供するデフォルトの物理エンジンです。アニメーションが速く始まり、目標地点に到達すると自然に減速します。
{
in: (element) => ({
physics: {
spring: {
stiffness: 300, // スプリング剛性 (高いほど速い)
damping: 30, // 減衰係数 (高いほど弾性が減少)
doubleSpring: false // ダブルスプリング効果
}
},
css: (progress) => ({
opacity: progress,
transform: `translateY(${(1 - progress) * 20}px)`,
}),
})
}
Inertia (慣性物理)
ease-in効果を提供する物理エンジンです。アニメーションがゆっくり始まり、徐々に加速します。
{
in: (element) => ({
physics: {
inertia: {
acceleration: 500, // 加速度 (高いほど速く加速)
resistance: 10 // 抵抗係数 (高いほど減速)
}
},
css: (progress) => ({
opacity: progress,
transform: `scale(${0.8 + progress * 0.2})`,
}),
})
}
Custom Integrator (カスタム物理エンジン)
独自の物理エンジンを実装して使用できます。
{
in: (element) => ({
physics: {
integrator: () => ({
next: (dt, state) => {
// カスタム物理計算ロジック
return newState;
}
})
},
css: (progress) => ({
opacity: progress,
}),
})
}
アニメーションモード: css vs tick
SSGOIは2つのアニメーションモードをサポートしています:
cssモード(推奨)
{
in: (element) => ({
physics: { spring: { stiffness: 300, damping: 30 } },
css: (progress) => ({
opacity: progress,
transform: `translateY(${(1 - progress) * 20}px)`,
}),
})
}
- Web Animation APIを使用してメインスレッドの負荷を最小化
- スプリング物理演算を事前計算してキーフレームに変換
- GPUアクセラレーションによるスムーズなアニメーション
- パフォーマンスが優れているため、ほとんどの場合推奨
tickモード
{
in: (element) => ({
physics: { spring: { stiffness: 300, damping: 30 } },
tick: (progress) => {
element.style.opacity = String(progress);
element.style.transform = `translateY(${(1 - progress) * 20}px)`;
},
})
}
- requestAnimationFrameベースで毎フレームコールバックを実行
- 直接的なDOM操作が必要な複雑なアニメーションに適している
- アニメーション中に動的に値を変更する必要がある場合に有用
cssとtickは同時に使用できません。どちらか1つを選択してください。
トランジションの定義
interface Transition {
in?: (element: HTMLElement) => TransitionConfig;
out?: (element: HTMLElement) => TransitionConfig;
}
動作の仕組み
- マウント時: 要素がDOMに追加されたとき
in関数を実行 - アンマウント時: 要素が削除される前に
out関数を実行 - アニメーション: スプリング物理エンジンがprogressを生成
- in: 0 → 1
- out: 1 → 0
- tickコールバック: 毎フレーム呼び出されてスタイルを更新
トランジションプリセット
import { fade, scale /** etc */ } from "@ssgoi/react/transitions";
フレームワーク別の使用方法
import { transition } from "@ssgoi/react";
<div
ref={transition({
key: "unique-key",
in: (element) => ({
tick: (progress) => {
element.style.opacity = progress;
element.style.transform = `translateY(${20 * (1 - progress)}px)`;
},
}),
out: (element) => ({
tick: (progress) => {
element.style.opacity = 1 - progress;
},
}),
})}
>
コンテンツ
</div>
Progressの動作
key
keyはページ内で一意である必要があります(DOMが作成された後削除されたり、削除された後作成されたりしてもアニメーション状態を追跡できるようにするため)- React: Auto Key Pluginを使用すると、
keyを省略可能(詳細はReactタブのコメントを参照) - その他のフレームワーク:
keyは必須です
inアニメーション
- progress: 0 → 1
- 要素が表示されるときに実行
- 不透明度が0から1へ、小さいサイズから元のサイズへ
outアニメーション
- progress: 1 → 0
- 要素が消えるときに実行
- 不透明度が1から0へ、元のサイズから小さいサイズへ
// 例: inとoutの違い
{
in: (element) => ({
tick: (progress) => {
// progress: 0 → 1
element.style.opacity = progress; // 0 → 1
}
}),
out: (element) => ({
tick: (progress) => {
// progress: 1 → 0
element.style.opacity = progress; // 1 → 0
}
})
}
prepareコールバック
アニメーション開始前にDOM要素を準備する段階:
{
in: {
prepare: (element) => {
// tick実行前に初期状態を設定
element.style.willChange = 'opacity, transform';
},
tick: (progress) => ({
opacity: progress,
transform: `translateY(${20 * (1 - progress)}px)`
})
}
}
TransitionScope
TransitionScopeは子要素のアニメーション動作を制御するコンテナです。親と子が同時にマウント/アンマウントされるときに不要なアニメーションをスキップできます。
使用シナリオ
- ページ遷移: ページ遷移時に個々の要素が別々にアニメーションしないようにする
- モーダル/ダイアログ: コンテナと一緒に表示されるコンテンツの重複アニメーションを防止
- リスト: リスト全体がマウント/アンマウントされるときに個々のアイテムのアニメーションをスキップ
scopeオプション
interface TransitionOptions {
// ...
scope?: 'global' | 'local';
}
global(デフォルト): 常にアニメーションを実行local: TransitionScopeと同時にマウント/アンマウントされるときはアニメーションをスキップ
使用例
import { TransitionScope, transition } from '@ssgoi/react';
function MyComponent() {
const [show, setShow] = useState(true);
return (
<>
<button onClick={() => setShow(!show)}>Toggle</button>
{show && (
<TransitionScope>
<div style={{ padding: '2rem', border: '1px dashed #ccc' }}>
{/* local: Scopeと同時にマウント/アンマウント時はアニメーションをスキップ */}
<div
ref={transition({
key: 'local-child',
scope: 'local',
in: () => ({
css: (p) => ({ opacity: p, transform: `scale(${0.5 + p * 0.5})` }),
}),
out: () => ({
css: (p) => ({ opacity: p, transform: `scale(${0.5 + p * 0.5})` }),
}),
})}
>
Local Scope(Scopeと一緒に表示/非表示)
</div>
{/* global: 常にアニメーションを実行 */}
<div
ref={transition({
key: 'global-child',
// scope: 'global'はデフォルト
in: () => ({
css: (p) => ({ opacity: p, transform: `scale(${0.5 + p * 0.5})` }),
}),
out: () => ({
css: (p) => ({ opacity: p, transform: `scale(${0.5 + p * 0.5})` }),
}),
})}
>
Global Scope(常にアニメーション)
</div>
</div>
</TransitionScope>
)}
</>
);
}
動作
| 状況 | scope: 'local' | scope: 'global' |
|---|---|---|
| Scopeと同時にマウント | アニメーションをスキップ | アニメーションを実行 |
| Scopeと同時にアンマウント | アニメーションをスキップ | アニメーションを実行 |
| Scope内で個別にトグル | アニメーションを実行 | アニメーションを実行 |