motion
Motion(旧Motion One)のバニラJavaScriptアニメーションライブラリを使用して、パフォーマンスに優れたアニメーションを構築するための専門的なガイドラインを提供します。アニメーションの実装方法やベストプラクティスについて的確なサポートを行います。
description の原文を見る
Expert guidelines for building performant animations with Motion (formerly Motion One) vanilla JavaScript animation library
SKILL.md 本文
Motion アニメーションガイドライン
Motion (motion.dev)、JavaScript、TypeScript、Webアニメーションパフォーマンスの専門家です。アニメーションを作成する際は、以下のガイドラインに従ってください。
コア原則
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(".element", { x: 100, opacity: 1 }, { duration: 0.5 });
// オプション付きアニメーション
animate(
".element",
{ transform: "translateX(100px)" },
{
duration: 0.8,
easing: "ease-out"
}
);
キーフレーム
animate(
".element",
{
x: [0, 100, 50], // キーフレーム値
opacity: [0, 1, 0.5]
},
{ duration: 1 }
);
パフォーマンス最適化
トランスフォーム プロパティのアニメーション
// 最高のパフォーマンス - GPU アクセラレーション
animate(".element", {
x: 100, // translateX
y: 50, // translateY
scale: 1.2, // scale
rotate: 45, // rotate
opacity: 0.5 // opacity
});
// 可能な限り回避 - レイアウト再計算を引き起こす
animate(".element", {
width: 200, // レイアウト再計算を引き起こす
height: 150, // レイアウト再計算を引き起こす
top: 50, // レイアウト再計算を引き起こす
left: 100 // レイアウト再計算を引き起こす
});
will-change を使用
// トランスフォーム アニメーション用に will-change を追加
const element = document.querySelector(".element");
element.style.willChange = "transform";
animate(element, { x: 100 }, {
onComplete: () => {
element.style.willChange = "auto"; // アニメーション後に削除
}
});
ハードウェア アクセラレーション
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();
// 特定の時刻にシーク
controls.currentTime = 0.5;
スクロールアニメーション
基本的なスクロールアニメーション
import { scroll, animate } from "motion";
scroll(
animate(".progress-bar", { scaleX: [0, 1] }),
{ target: document.querySelector("article") }
);
スクロールリンク アニメーション
scroll(({ y }) => {
// y.progress は 0 から 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 () => {
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", // ビューポート進入前 100px でトリガー
amount: 0.5 // 50% 表示時にトリガー
}
);
スタッガーアニメーション
複数要素のスタッガー
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",
// またはカスタムスプリング設定
easing: [0.34, 1.56, 0.64, 1] // カスタムベジェ曲線
}
);
スプリングオプション
animate(".element", { x: 100 }, {
type: "spring",
stiffness: 300,
damping: 30
});
イージング関数
組み込みイージング
// 一般的なイージング値
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" });
// 3次ベジェ曲線
animate(".element", { x: 100 }, {
easing: [0.25, 0.1, 0.25, 1]
});
アニメーションコントロール
再生制御
const controls = animate(".element", { x: 100 }, { duration: 1 });
// コントロールメソッド
controls.play();
controls.pause();
controls.stop();
controls.finish();
controls.reverse();
// 時刻の取得/設定
controls.currentTime = 0.5;
console.log(controls.duration);
// アニメーションをキャンセル
controls.cancel();
アニメーションイベント
const controls = animate(
".element",
{ x: 100 },
{
duration: 1,
onComplete: () => console.log("完了!")
}
);
// Promise ベース
controls.finished.then(() => {
console.log("アニメーション完了");
});
アクセシビリティ
モーション削減を尊重
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 });
// その後、キャンセル
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() を使用
- インタラクティブ/ジェスチャーアニメーションにはスプリングを優先
- 常にモーション削減設定を尊重
- 不要なアニメーションはキャンセル
- 実際のデバイスでパフォーマンスをテスト
ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- mindrally
- リポジトリ
- mindrally/skills
- ライセンス
- Apache-2.0
- 最終更新
- 不明
Source: https://github.com/mindrally/skills / ライセンス: Apache-2.0
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。