重さと弾み(Physics)

スプリング・慣性・イージングを使った物理ベースのアニメーションをFramer Motion / React Spring / GSAP / CSS Transitionで実装・比較

Framer Motion

読み込み中...

tsx
'use client';
import { useState } from 'react';
import { motion, useAnimation } from 'framer-motion';

// スプリング — ボタンでバネのように弾んで移動
function SpringDemo() {
  const [toggled, setToggled] = useState(false);
  return (
    <div>
      <motion.div
        animate={{ x: toggled ? 160 : 0 }}
        transition={{ type: 'spring', stiffness: 200, damping: 10 }}
        style={{ width: '3.5rem', height: '3.5rem', borderRadius: '0.75rem', background: '#f43f5e' }}
      />
      <button onClick={() => setToggled((v) => !v)}>スプリング</button>
    </div>
  );
}

// 慣性 — ドラッグ後に慣性で滑るように止まる
function InertiaDemo() {
  return (
    <motion.div
      drag="x"
      dragConstraints={{ left: 0, right: 200 }}
      dragTransition={{ power: 0.3, timeConstant: 200 }}
      whileDrag={{ scale: 1.1 }}
      style={{ width: '3.5rem', height: '3.5rem', borderRadius: '0.75rem', background: '#8b5cf6', cursor: 'grab' }}
    />
  );
}

// イージング比較 — linear / easeOut / easeInOut / spring を並べて比較
const EASING_ITEMS = [
  { label: 'linear',    color: '#0ea5e9', transition: { type: 'tween', duration: 1.0, ease: 'linear' } },
  { label: 'easeOut',   color: '#10b981', transition: { type: 'tween', duration: 1.0, ease: 'easeOut' } },
  { label: 'easeInOut', color: '#f97316', transition: { type: 'tween', duration: 1.0, ease: 'easeInOut' } },
  { label: 'spring',    color: '#f43f5e', transition: { type: 'spring', stiffness: 120, damping: 12 } },
] as const;

function EasingCompareDemo() {
  const controls = useAnimation();
  const [direction, setDirection] = useState<1 | -1>(1);

  const run = async () => {
    await controls.start((i) => ({
      x: direction * 160,
      transition: EASING_ITEMS[i].transition,
    }));
    setDirection((d) => (d === 1 ? -1 : 1));
  };

  return (
    <div>
      {EASING_ITEMS.map((item, i) => (
        <motion.div
          key={item.label}
          custom={i}
          animate={controls}
          initial={{ x: 0 }}
          style={{ width: '2rem', height: '2rem', background: item.color }}
        />
      ))}
      <button onClick={run}>動かす</button>
    </div>
  );
}

jQueryで書くとこうなる

jQueryの .animate() はデフォルトで swing(ease相当)と linear のみ対応しており、スプリングのような物理ベースのアニメーションにはjQuery UIのeasingプラグインや自前のループ処理が必要でした。Reactでは Framer Motion・React Spring の物理演算や GSAP の elastic.out イージングを使うことで、重さと弾みのある物理挙動が簡単に実現できます。
js
// easing を指定した animate()(jQuery UI が必要)
$('#box').animate({ left: '200px' }, {
  duration: 600,
  easing: 'easeOutBounce',
});

// swing(jQueryデフォルトのイージング)
$('#box').animate({ left: '200px' }, 600, 'swing');

// linear
$('#box').animate({ left: '200px' }, 600, 'linear');

// jQuery単体でのスプリング再現(手動ループ)
function springAnimate(el, target, stiffness, damping) {
  let pos = 0, vel = 0;
  function step() {
    const force = (target - pos) * stiffness;
    vel = (vel + force) * damping;
    pos += vel;
    $(el).css('transform', `translateX(${pos}px)`);
    if (Math.abs(vel) > 0.1) requestAnimationFrame(step);
  }
  step();
}
💡 上記のデモは、React / Next.jsです。Next.jsの基本セットアップは 公式ドキュメントを参照してください。

実装方法の比較

項目Framer MotionReact SpringGSAPCSS Transition
スプリングstiffness・dampingで細かく制御可能tension・frictionで細かく制御可能(Framer相当)elastic.out / back.outで近似(物理ベースではない)再現不可(cubic-bezierで近似のみ)
慣性type="inertia"でドラッグ後の滑りを自動計算@use-gesture/reactと組み合わせて実装InertiaPlugin(有料Club GSAP)が必要再現不可
連鎖アニメーションstagger・delayで順番に実行useTrailで複数要素を自動的に遅延させて順番に動かすstaggerオプションで複数要素を自動遅延animation-delayで手動設定
設定コスト高:framer-motionのインストールが必要高:@react-spring/webのインストールが必要中:gsap + @gsap/reactのインストールが必要低:追加ライブラリ不要
物理ベースの動きネイティブサポートネイティブサポート(バネ物理学が設計の核心)イージング曲線での近似(真の物理演算は非対応)非対応(近似のみ)

スプリングアニメーションのポイント(Framer Motion)

  • stiffness(バネの硬さ)が高いほど素早く動き、低いほどゆっくり動く
  • damping(減衰)が低いほど長く揺れ続け、高いほどすぐ止まる
  • mass(質量)を追加するとさらに重厚感のある動きになる
  • • CSSの cubic-bezier では静止後の揺れ(オーバーシュート)の再現が困難
  • • ドラッグ後の慣性には dragTransitionpowertimeConstant で挙動を調整できる

🤖 AIプロンプトテンプレート

Next.js + Framer Motion で物理ベースのアニメーションを実装してください。
- スプリングアニメーションでボタンクリック時に要素が弾んで移動する
- stiffness・dampingを調整して動きの硬さ・柔らかさを表現する
- linear・easeOut・springの動きをを並べて比較できるデモにする
- TypeScript / Tailwind CSS v4 を使用

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

React Spring のポイント

  • tension(バネの強さ)が高いほど素早く動き、friction(摩擦)が高いほど早く止まる
  • • Framer Motion の stiffnesstensiondampingfriction と考えてよい
  • config.wobbly(揺れ多め)・config.gentle(滑らか)・config.stiff(キビキビ)などのプリセットが便利
  • useTrail は複数要素を一定の遅延で順番にアニメーションさせる専用フック

🤖 AIプロンプトテンプレート

Next.js + React Spring で物理ベースのアニメーションを実装してください。
- useSpring を使ってスプリングアニメーションでボタンクリック時に要素が弾んで移動する
- tension・frictionを調整して動きの硬さ・柔らかさを表現する
- useTrail を使って複数要素が順番に遅延しながら表示されるアニメーションを実装
- config.gentle / config.wobbly / config.stiff を比較するデモにする
- 使用ライブラリ: @react-spring/web
- TypeScript / Tailwind CSS v4 を使用

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

GSAP のポイント

  • • GSAP はイージング曲線ベースのため、真の物理演算(Framer・React Spring)とは仕組みが異なる
  • elastic.out(amplitude, period) でオーバーシュートする弾む動きを表現できる(amplitude: 振れ幅、period: 振動の頻度)
  • gsap.to(elements, { stagger: 0.08 }) で複数要素を一定間隔で順番にアニメーションさせる(React Spring の useTrail に相当)
  • • 慣性ドラッグには有料の InertiaPlugin(Club GSAP)が必要。無料版では @use-gesture/react との組み合わせが現実的

🤖 AIプロンプトテンプレート

Next.js + GSAP で重さと弾みのアニメーションを実装してください。
- useGSAP フックと contextSafe を使ってボタンクリックでアニメーションをトリガー
- elastic.out(1, 0.4) でスプリング風の弾む動きを実装
- linear / power2.out / elastic.out を並べてイージング比較できるデモにする
- gsap.to() の stagger オプションで複数要素を順番にアニメーションさせる
- 使用ライブラリ: gsap, @gsap/react
- TypeScript / Tailwind CSS v4 を使用

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。

重さと弾み(Physics)は、物理法則に基づいたイージング・スプリング・バウンスを用いて、UIの動きをリアルで心地よく感じさせるアニメーション効果。

カード操作のドラッグ・ドロップ・メニューの展開収縮・スクロール連動のパララックス・フォームフィールドのフォーカスアニメーションなど、インタラクションを有機的に見せたい場面で使われる。

主なバリエーション
  • スプリング型(バネのような動き)
  • イナーシャ型(慣性あり)
  • バウンス型(跳ね返り)
  • グラビティ型(重力感)
  • フリクション型(摩擦あり)