ドラッグ(慣性・バウンド)
物理シミュレーションの有無と精度・慣性アルゴリズムの実装差を主要ライブラリで比較
💻 PC でご覧の方へ:3 つとも PC マウスに対応。フリック(素早くドラッグして離す)で慣性・バウンドの物理挙動が確認できます。
Framer Motion
読み込み中...
tsx
'use client';
import { motion } from 'framer-motion';
export function FramerDragDemo() {
return (
<div className="relative h-64 bg-gray-100 rounded-lg overflow-hidden flex items-center justify-center">
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -80, bottom: 80 }}
dragElastic={0.2}
dragTransition={{ bounceStiffness: 300, bounceDamping: 20 }}
whileDrag={{ scale: 1.1 }}
className="w-16 h-16 bg-rose-500 rounded-xl cursor-grab active:cursor-grabbing flex items-center justify-center text-white font-bold"
>
ドラッグ
</motion.div>
</div>
);
}物理シミュレーション比較表
| 項目 | Framer Motion | @use-gesture/react | Pointer Events API |
|---|---|---|---|
| 慣性(フリック後) | dragTransition で自動(power・timeConstant) | velocity 取得 → 自前 RAF 実装 | Δpos/Δt 速度 → 完全自前 RAF 実装 |
| 減衰モデル | 内部 Spring 物理(timeConstant で時定数制御) | v *= DAMPING(指数関数的・手実装) | v *= DAMPING(指数関数的・手実装) |
| バウンド実装 | bounceStiffness / bounceDamping(Spring 物理) | v = -v × RESTITUTION(手実装) | v = -v × RESTITUTION(手実装) |
| 壁の弾性 | dragElastic 係数(0 = 固定 / 1 = 完全弾性) | rubberband オプション(距離 → スケール変換) | 実装次第(本デモは即座停止) |
| 速度取得精度 | useVelocity(MotionValue を時間微分・リアルタイム) | EMA フィルタ済み velocity(use-gesture が平滑化) | 生 Δpos/Δt(手動 LPF 実装が必要) |
| 物理パラメータ可視性 | 4パラメータ(power / timeConst / bounce×2) | DAMPING・RESTITUTION を自分で定義 | 完全自由(物理方程式を直接記述) |
| 実装コスト | 低:設定のみ・物理コード不要 | 中:速度取得は楽・慣性は手実装 | 高:速度計算・LPF・慣性・バウンド全手実装 |
物理シミュレーションの精度と制御
Framer Motion — Spring 物理エンジン内蔵
慣性は power × velocity × timeConstant で目標位置を計算し、バウンドは bounceStiffness / bounceDamping の Spring で制御。物理方程式を一切書かずに自然な挙動が得られる。useVelocity で慣性フェーズ中もリアルタイムに速度を取得できる点が強力。
@use-gesture/react — 速度取得のみ担当
velocity は EMA で平滑化済みの高精度値を提供するが、慣性・バウンドの物理ループは開発者が実装する。v *= DAMPING の減衰定数を直接制御できるため、ゲーム的な強いバウンドや摩擦感のある動きなど、物理感覚を細かく調整したい場合に適する。
Pointer Events API — 物理方程式の直接記述
速度は Δpos / Δt の生値のため LPF(ローパスフィルタ)の手実装が必要。フリック終了直前の指の動きが止まっている場合、速度がほぼ 0 になり慣性が発生しないことがある。EMA weight・DAMPING・RESTITUTION のすべてを直接制御できる代わりに、実装コストと品質維持の責任が開発者側に全面的にかかる。
⚠️ iOS Safari での注意点
- • ドラッグ要素に
touch-action: noneが必須(ページスクロールとの競合回避) - • Framer の
motionコンポーネントは内部で自動付与。use-gesture・Vanilla は手動指定が必要 - • Vanilla 実装では
setPointerCaptureでポインタをキャプチャしないと、指が要素外に出た瞬間にドラッグが途切れる - •
pointercancelイベントを必ず処理しておかないと、電話着信などシステム割り込み時に慣性が永久ループする可能性がある - • iOS の
scroll-snapやoverscroll-behaviorが干渉する場合は、祖先要素の CSS を確認する
🤖 AIプロンプトテンプレート
Reactでドラッグ(慣性・バウンド付き)を実装してください。以下の要件を満たしてください。 - Framer Motion、@use-gesture/react、またはPointer Events APIのいずれかを使用すること - マウスとタッチの両方でドラッグ操作ができること - フリック(素早くドラッグして離す)で慣性アニメーションが発生すること - 境界(コンテナ枠)でバウンス(弾性反発)するアニメーションを実装すること - dragElasticまたは同等のゴム引き効果(境界外での抵抗感)を実装すること - iOS Safariでtouch-action: noneを適切に設定し、ページスクロールとの競合を避けること
⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。