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 和 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 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. 优先使用 x、y,而不是 left、top
  2. 优先使用 scale,而不是 width、height
  3. 使用 opacity 实现淡入淡出效果
  4. 优先使用 rotate,而不是 transform: 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. 使用 timeline 创建序列动画
  4. 使用 scroll() 实现滚动联动效果
  5. 使用 inView() 实现视口触发的动画
  6. 使用 stagger() 为多个元素设置动画
  7. 交互式/手势动画优先使用弹簧
  8. 始终遵循减少动态效果偏好设置
  9. 不再需要动画时将其取消
  10. 在实际设备上测试性能

评论

支持 Markdown