mcpField
M
motion

motion

github.com/mindrallyデザイン・UI288827 回インストール3 週間前に更新
リポジトリ

motion について

JavaScript と TypeScript のウェブ開発者向け Motion アニメーションスキルです。アニメーションの構成、スクロールやビューポート連動、再生制御、性能、視覚効果を減らす設定を扱います。

主な機能

  1. 01要素とキーフレームのアニメーション
  2. 02タイムラインの構成と制御
  3. 03スクロール連動とビューポート効果
  4. 04スタガーとスプリングアニメーション
  5. 05再生イベントとキャンセル
  6. 06性能と視覚効果を減らす設定

活用シーン

  1. 01ページ要素やシーケンスのアニメーション
  2. 02スクロールに連動する効果の作成
  3. 03要素のビューポート進入時にアニメーションを開始
  4. 04視覚効果を減らす設定への対応

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindrally/skills --skill motion

インストール後、Claude Code で/motionを入力して読み込まれたか確認します。

詳細

Motion アニメーションのガイドライン

Motion(motion.dev)、JavaScript、TypeScript、ウェブアニメーションのパフォーマンスに精通した専門家として、アニメーションを作成する際は以下のガイドラインに従ってください。

基本原則

Motion について
  • Motion は、Framer Motion の開発者が提供する JavaScript アニメーションライブラリです
  • バニラ JavaScript/TypeScript プロジェクトでは motion を使用します
  • React プロジェクトでは motion/react を使用します(framer-motion スキルを参照)
  • バンドルサイズを最小限に抑えながら、高いパフォーマンスを実現するよう設計されています
インストール
npm install motion
基本的なインポート
import { animate, scroll, inView, timeline } from "motion";

基本的なアニメーション

シンプルなアニメーション
import { animate } from "motion";

// Animate a single element
animate(".element", { x: 100, opacity: 1 }, { duration: 0.5 });

// Animate with options
animate(
  ".element",
  { transform: "translateX(100px)" },
  {
    duration: 0.8,
    easing: "ease-out"
  }
);
キーフレーム
animate(
  ".element",
  {
    x: [0, 100, 50],      // Keyframe values
    opacity: [0, 1, 0.5]
  },
  { duration: 1 }
);

パフォーマンスの最適化

トランスフォームプロパティをアニメーション化する
// Best performance - GPU accelerated
animate(".element", {
  x: 100,           // translateX
  y: 50,            // translateY
  scale: 1.2,       // scale
  rotate: 45,       // rotate
  opacity: 0.5      // opacity
});

// Avoid when possible - triggers layout
animate(".element", {
  width: 200,       // Causes layout recalculation
  height: 150,      // Causes layout recalculation
  top: 50,          // Causes layout recalculation
  left: 100         // Causes layout recalculation
});
will-change を使用する
// Add will-change for transform animations
const element = document.querySelector(".element");
element.style.willChange = "transform";

animate(element, { x: 100 }, {
  onComplete: () => {
    element.style.willChange = "auto"; // Remove after animation
  }
});
ハードウェアアクセラレーション

可能な場合、Motion はハードウェアアクセラレーションに対応したプロパティを自動的に使用します。最高のパフォーマンスを得るには、次の点を守ってください。

  1. left、top よりも x、y を優先します
  2. width、height よりも scale を優先します
  3. フェード効果には opacity を使用します
  4. transform: rotate() よりも rotate を優先します

タイムラインアニメーション

タイムラインを作成する
import { timeline } from "motion";

const sequence = [
  [".header", { y: ["-100%", 0], opacity: [0, 1] }],
  [".content", { y: [50, 0], opacity: [0, 1] }, { at: "-0.3" }],
  [".footer", { y: [50, 0], opacity: [0, 1] }, { at: "-0.3" }]
];

const controls = timeline(sequence, {
  duration: 0.8,
  defaultOptions: { easing: "ease-out" }
});
タイムラインの制御
const controls = timeline(sequence);

controls.play();
controls.pause();
controls.reverse();
controls.stop();
controls.finish();

// Seek to specific time
controls.currentTime = 0.5;

スクロールアニメーション

基本的なスクロールアニメーション
import { scroll, animate } from "motion";

scroll(
  animate(".progress-bar", { scaleX: [0, 1] }),
  { target: document.querySelector("article") }
);
スクロールに連動したアニメーション
scroll(({ y }) => {
  // y.progress is 0 to 1
  animate(".element", {
    opacity: y.progress,
    y: y.progress * 100
  }, { duration: 0 });
});
コンテナーを使ったスクロール
scroll(
  animate(".parallax", { y: [0, -100] }),
  {
    target: document.querySelector(".section"),
    offset: ["start end", "end start"]
  }
);

表示範囲内でのアニメーション

表示されたときにトリガーする
import { inView, animate } from "motion";

inView(".card", (info) => {
  animate(info.target, { opacity: 1, y: 0 }, { duration: 0.5 });

  // Return cleanup function
  return () => {
    animate(info.target, { opacity: 0, y: 20 }, { duration: 0.2 });
  };
});
オプションを指定する
inView(
  ".element",
  (info) => {
    animate(info.target, { scale: [0.8, 1], opacity: [0, 1] });
  },
  {
    margin: "-100px",  // Trigger 100px before entering viewport
    amount: 0.5        // Trigger when 50% visible
  }
);

スタッガーアニメーション

複数の要素にスタッガーを適用する
import { stagger, animate } from "motion";

animate(
  ".list-item",
  { opacity: [0, 1], y: [20, 0] },
  { delay: stagger(0.1) }
);
中央からスタッガーを適用する
animate(
  ".grid-item",
  { scale: [0, 1] },
  { delay: stagger(0.05, { from: "center" }) }
);
イージングを使ったスタッガー
animate(
  ".item",
  { x: ["-100%", 0] },
  {
    delay: stagger(0.1, {
      easing: "ease-out",
      start: 0.2
    })
  }
);

スプリングアニメーション

自然な動きにはスプリングを使用する
animate(
  ".element",
  { scale: 1.2 },
  {
    easing: "spring",
    // or with custom spring settings
    easing: [0.34, 1.56, 0.64, 1] // Custom bezier curve
  }
);
スプリングのオプション
animate(".element", { x: 100 }, {
  type: "spring",
  stiffness: 300,
  damping: 30
});

イージング関数

組み込みのイージング
// Common easing values
animate(".element", { x: 100 }, { easing: "ease" });
animate(".element", { x: 100 }, { easing: "ease-in" });
animate(".element", { x: 100 }, { easing: "ease-out" });
animate(".element", { x: 100 }, { easing: "ease-in-out" });
animate(".element", { x: 100 }, { easing: "linear" });

// Cubic bezier
animate(".element", { x: 100 }, {
  easing: [0.25, 0.1, 0.25, 1]
});

アニメーションの制御

再生を制御する
const controls = animate(".element", { x: 100 }, { duration: 1 });

// Control methods
controls.play();
controls.pause();
controls.stop();
controls.finish();
controls.reverse();

// Get/set time
controls.currentTime = 0.5;
console.log(controls.duration);

// Cancel animation
controls.cancel();
アニメーションイベント
const controls = animate(
  ".element",
  { x: 100 },
  {
    duration: 1,
    onComplete: () => console.log("Done!")
  }
);

// Promise-based
controls.finished.then(() => {
  console.log("Animation finished");
});

アクセシビリティ

モーション低減設定を尊重する
const prefersReducedMotion = window.matchMedia(
  "(prefers-reduced-motion: reduce)"
).matches;

animate(
  ".element",
  { x: 100, opacity: 1 },
  {
    duration: prefersReducedMotion ? 0 : 0.5,
    easing: prefersReducedMotion ? "linear" : "ease-out"
  }
);
アクセシブルなラッパーを作成する
function safeAnimate(element, keyframes, options = {}) {
  const reducedMotion = window.matchMedia(
    "(prefers-reduced-motion: reduce)"
  ).matches;

  return animate(element, keyframes, {
    ...options,
    duration: reducedMotion ? 0 : (options.duration ?? 0.3)
  });
}

フレームワークとの統合

バニラ JavaScript
document.addEventListener("DOMContentLoaded", () => {
  animate(".hero", { opacity: [0, 1], y: [30, 0] });
});
イベントリスナーを使う
const button = document.querySelector(".button");

button.addEventListener("mouseenter", () => {
  animate(button, { scale: 1.05 }, { duration: 0.2 });
});

button.addEventListener("mouseleave", () => {
  animate(button, { scale: 1 }, { duration: 0.2 });
});

クリーンアップ

アニメーションをキャンセルする
const controls = animate(".element", { x: 100 });

// Later, cancel it
controls.cancel();
クリーンアップのパターン
class AnimatedComponent {
  constructor(element) {
    this.element = element;
    this.animations = [];
  }

  animate(keyframes, options) {
    const controls = animate(this.element, keyframes, options);
    this.animations.push(controls);
    return controls;
  }

  destroy() {
    this.animations.forEach(anim => anim.cancel());
    this.animations = [];
  }
}

ベストプラクティスのまとめ

  1. 最高のパフォーマンスを得るため、トランスフォームプロパティ(x、y、scale、rotate)を使用します
  2. 複雑なアニメーションの前に will-change を追加し、終了後に削除します
  3. 順序立てたアニメーションにはタイムラインを使用します
  4. スクロールに連動した効果には scroll() を使用します
  5. ビューポートをトリガーにするアニメーションには inView() を使用します
  6. 複数の要素をアニメーション化するには stagger() を使用します
  7. インタラクティブなアニメーションやジェスチャーアニメーションには、スプリングを優先します
  8. モーション低減の設定を常に尊重します
  9. 不要になったアニメーションはキャンセルします
  10. 実際のデバイスでパフォーマンスをテストします

コメント