motion について
JavaScript と TypeScript のウェブ開発者向け Motion アニメーションスキルです。アニメーションの構成、スクロールやビューポート連動、再生制御、性能、視覚効果を減らす設定を扱います。
主な機能
- 01要素とキーフレームのアニメーション
- 02タイムラインの構成と制御
- 03スクロール連動とビューポート効果
- 04スタガーとスプリングアニメーション
- 05再生イベントとキャンセル
- 06性能と視覚効果を減らす設定
活用シーン
- 01ページ要素やシーケンスのアニメーション
- 02スクロールに連動する効果の作成
- 03要素のビューポート進入時にアニメーションを開始
- 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 はハードウェアアクセラレーションに対応したプロパティを自動的に使用します。最高のパフォーマンスを得るには、次の点を守ってください。
left、topよりもx、yを優先しますwidth、heightよりもscaleを優先します- フェード効果には
opacityを使用します 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 = [];
}
}
ベストプラクティスのまとめ
- 最高のパフォーマンスを得るため、トランスフォームプロパティ(x、y、scale、rotate)を使用します
- 複雑なアニメーションの前に will-change を追加し、終了後に削除します
- 順序立てたアニメーションにはタイムラインを使用します
- スクロールに連動した効果には scroll() を使用します
- ビューポートをトリガーにするアニメーションには inView() を使用します
- 複数の要素をアニメーション化するには stagger() を使用します
- インタラクティブなアニメーションやジェスチャーアニメーションには、スプリングを優先します
- モーション低減の設定を常に尊重します
- 不要になったアニメーションはキャンセルします
- 実際のデバイスでパフォーマンスをテストします
コメント
Markdown 対応