Pinterestトランジション
カードが画面全体を満たしながら拡張する没入型トランジション
Pinterestトランジション
Pinterestトランジションは、ギャラリーの小さなアイテムが全画面に拡張する**拡張型トランジション(Expansion Transition)**です。ユーザーが選択したコンテンツが画面を満たしながら拡大され、強力な没入感と視覚的連続性を提供します。
デモ
Loading demo...
UXの原則
いつ使用するか?
Pinterestトランジションは視覚的コンテンツの拡大探索時に使用されます。
コンテンツの関係別適合性
콘텐츠 관계 | 적합성 | 설명 |
---|---|---|
関連のないコンテンツ | ❌ | 拡張する接続点がなく不適切 |
兄弟関係 | ⚠️ | ギャラリー内のアイテム間移動時に検討可能 |
階層関係 | ✅ | サムネイル→フルビューのような同じコンテンツの拡大ビューに最適 |
主な使用ケース
- 画像ギャラリー: サムネイルから高解像度画像へ拡大
- 製品カタログ: 製品グリッドから詳細情報へ拡張
- メディアブラウジング: ビデオサムネイルからプレーヤーへ遷移
- ポートフォリオ: 作品リストから全画面プレゼンテーションへ
なぜこのように動作するのか?
- 空間拡張メタファー: 小さな窓を通して大きな世界に入る感覚
- 自然なフォーカス移動: ユーザーの視線が自然に拡張する要素を追う
- コンテキスト保存: どこから来たかが明確で方向感覚を維持
- ドラマチックな効果: 拡張アニメーションがコンテンツの重要性を強調
モーションデザイン
拡張プロセス:
1. 選択されたカードの位置とサイズをキャプチャ
2. カードが画面中央へ移動しながら拡大
3. 同時に背景のフェードとスケール変化
4. 最終的に全画面を満たす
縮小プロセス:
1. 詳細画面が縮小しながら元の位置へ
2. 背景が再び現れてギャラリーが復元
3. 自然なスプリングアニメーションで定着
基本的な使い方
1. トランジション設定
import { Ssgoi } from '@ssgoi/react';
import { pinterest } from '@ssgoi/react/view-transitions';
const config = {
transitions: [
{
from: '/gallery',
to: '/gallery/*',
transition: pinterest(),
symmetric: true
}
]
};
export default function App() {
return (
<Ssgoi config={config}>
{/* アプリの内容 */}
</Ssgoi>
);
}
2. 要素のマーキング
ギャラリーと詳細ページにそれぞれ異なるdata属性を使用:
// ギャラリーページ
function Gallery() {
return (
<div className="masonry-grid">
{items.map(item => (
<Link
key={item.id}
to={`/gallery/${item.id}`}
data-pinterest-gallery-key={item.id}
className="gallery-item"
>
<img src={item.image} alt={item.title} />
<h3>{item.title}</h3>
</Link>
))}
</div>
);
}
// 詳細ページ
function ItemDetail({ item }) {
return (
<div
data-pinterest-detail-key={item.id}
className="detail-container"
>
<button onClick={() => navigate('/gallery')}>
✕ 閉じる
</button>
<img src={item.image} alt={item.title} />
<article>
<h1>{item.title}</h1>
<p>{item.description}</p>
</article>
</div>
);
}
実践的な活用例
1. メイソンリーギャラリー
Pinterestスタイルのメイソンリーレイアウト:
// メイソンリーグリッド
function MasonryGallery() {
return (
<div className="columns-2 md:columns-3 lg:columns-4 gap-4">
{images.map((img, index) => (
<div
key={img.id}
data-pinterest-gallery-key={img.id}
onClick={() => navigate(`/image/${img.id}`)}
className="break-inside-avoid mb-4 cursor-pointer
hover:scale-105 transition-transform"
>
<img
src={img.url}
alt={img.title}
className="w-full rounded-lg"
/>
<div className="p-2">
<p className="text-sm font-medium">{img.title}</p>
<p className="text-xs text-gray-500">{img.author}</p>
</div>
</div>
))}
</div>
);
}
// 拡張ビュー
function ImageView({ image }) {
return (
<div
data-pinterest-detail-key={image.id}
className="fixed inset-0 bg-white z-50 overflow-auto"
>
<div className="max-w-4xl mx-auto p-4">
<img
src={image.url}
alt={image.title}
className="w-full rounded-lg shadow-xl"
/>
<div className="mt-4">
<h1 className="text-2xl font-bold">{image.title}</h1>
<p className="text-gray-600 mt-2">{image.description}</p>
</div>
</div>
</div>
);
}
2. 製品カタログ
製品カードが詳細ページへ拡張:
// 製品グリッド
function ProductGrid() {
return (
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{products.map(product => (
<article
key={product.id}
data-pinterest-gallery-key={product.id}
onClick={() => navigate(`/product/${product.id}`)}
className="bg-white rounded-xl shadow-lg overflow-hidden
cursor-pointer hover:shadow-xl transition-shadow"
>
<img
src={product.image}
alt={product.name}
className="w-full h-48 object-cover"
/>
<div className="p-4">
<h3 className="font-semibold">{product.name}</h3>
<p className="text-lg font-bold mt-2">¥{product.price}</p>
</div>
</article>
))}
</div>
);
}
// 製品詳細
function ProductDetail({ product }) {
return (
<div
data-pinterest-detail-key={product.id}
className="min-h-screen bg-white"
>
<div className="grid md:grid-cols-2 gap-8 p-8">
<img
src={product.image}
alt={product.name}
className="w-full rounded-lg"
/>
<div>
<h1 className="text-3xl font-bold">{product.name}</h1>
<p className="text-2xl font-bold mt-4">¥{product.price}</p>
<p className="mt-4 text-gray-600">{product.description}</p>
<button className="mt-6 bg-black text-white px-8 py-3 rounded-lg">
カートに追加
</button>
</div>
</div>
</div>
);
}
カスタマイズ
スプリング設定
アニメーションの弾性と速度の調整:
pinterest({
spring: {
stiffness: 200, // 低いほど滑らか
damping: 25 // 高いほど速い定着
}
})
注意事項
キー属性の区別
- ギャラリー:
data-pinterest-gallery-key
- 詳細:
data-pinterest-detail-key
- 必ず異なる属性名を使用(ヒーローと差別化)
レイアウトの考慮事項
- ギャラリーアイテムはpositionがstaticである必要があります
- 詳細ページには十分な余白が必要
- メイソンリーレイアウトと特に相性が良い
アクセシビリティ
- ESCキーで詳細ページを閉じるサポート
- フォーカストラップによるキーボードナビゲーションの改善
- モーション削減設定時は即座に遷移
ベストプラクティス
✅ DO
- 画像中心のコンテンツに使用
- ギャラリーやカタログ形式に適用
- 閉じるボタンを明確に提供
- モバイルでも動作するようレスポンシブデザイン
❌ DON'T
- テキスト中心のコンテンツには不適切
- 多くの要素が同時に拡張されないように
- 詳細ページのロードが遅い場合は使用を控える
- 複雑なレイアウトでは予期しない動作の可能性