关于 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 和 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 会在可能的情况下自动使用硬件加速属性。为获得最佳性能:
- 优先使用
x、y,而不是left、top - 优先使用
scale,而不是width、height - 使用
opacity实现淡入淡出效果 - 优先使用
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 = [];
}
}
最佳实践摘要
- 使用变换属性(x、y、scale、rotate)以获得最佳性能
- 在复杂动画开始前添加 will-change,结束后移除
- 使用 timeline 创建序列动画
- 使用 scroll() 实现滚动联动效果
- 使用 inView() 实现视口触发的动画
- 使用 stagger() 为多个元素设置动画
- 交互式/手势动画优先使用弹簧
- 始终遵循减少动态效果偏好设置
- 不再需要动画时将其取消
- 在实际设备上测试性能
评论
支持 Markdown