关于 animejs-mastery
animejs-mastery 是一项自主 Agent 技能,可分析代码库并使用 anime.js 实现界面动画。它涵盖机会识别、可复用工具与 React hooks,并检查性能和减少动态效果偏好。
主要功能
- 01分析代码库和框架
- 02扫描动画应用机会
- 03创建共享动画工具
- 04编写可复用的 React hooks
- 05检查减少动态效果和性能
适用场景
- 01添加页面切换和加载动画
- 02为列表、模态框和表单反馈添加动画
- 03制作按钮和卡片交互效果
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ frogody/ app.isyncso --skill animejs-mastery安装后在 Claude Code 中输入/animejs-mastery确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api-reference.mdpatterns.mdscripts/analyze-for-animations.sh详细介绍
Anime.js 动画升级 Agent
一个自主 Agent,用于分析代码库,并使用 anime.js 系统地实现精致、高性能的动画。
Agent 工作流
按顺序执行以下阶段。自主完成工作——不要在步骤之间请求许可。
阶段 1:代码库分析
# 1. Identify framework and structure
find . -name "package.json" -o -name "*.tsx" -o -name "*.jsx" -o -name "*.vue" -o -name "*.svelte" | head -20
# 2. Check if anime.js is installed
grep -r "animejs\|anime.js" package.json 2>/dev/null || echo "NOT_INSTALLED"
# 3. Map component structure
find . -type f \( -name "*.tsx" -o -name "*.jsx" -o -name "*.vue" \) -path "*/components/*" | head -50
如果尚未安装 anime.js,请安装:
npm install animejs
# For TypeScript projects:
npm install --save-dev @types/animejs
阶段 2:识别机会
扫描代码库,寻找以下高影响力的动画机会:
| 优先级 | 查找模式 | 动画类型 |
|---|---|---|
| P0 | 页面/路由过渡 | 淡入淡出 + 滑动序列 |
| P0 | 加载状态、旋转指示器 | 平滑脉动/旋转 |
| P1 | 渲染数据的列表 | 交错入场 |
| P1 | 模态框/对话框/抽屉 | 缩放 + 淡入 |
| P1 | 表单提交 | 成功/错误反馈 |
| P2 | 按钮、CTA | 悬停/按压微交互 |
| P2 | 卡片、平铺元素 | 悬停抬升效果 |
| P2 | 数据可视化 | 数字递增、图表显现 |
| P3 | 图标、徽章 | 提醒脉动 |
| P3 | 工具提示、弹出层 | 柔和入场 |
搜索模式:
# Find loading states
grep -rn "loading\|isLoading\|skeleton\|Spinner" --include="*.tsx" --include="*.jsx"
# Find modals/dialogs
grep -rn "Modal\|Dialog\|Drawer\|Sheet" --include="*.tsx" --include="*.jsx"
# Find lists with map
grep -rn "\.map(" --include="*.tsx" --include="*.jsx" | grep -i "item\|card\|row"
# Find buttons
grep -rn "<button\|<Button\|onClick" --include="*.tsx" --include="*.jsx"
# Find route transitions
grep -rn "Route\|router\|navigate\|Link" --include="*.tsx" --include="*.jsx"
阶段 3:创建动画工具
首先创建共享的动画工具文件:
// src/lib/animations.ts (or utils/animations.ts)
import anime from 'animejs';
// Respect user preferences
export const prefersReducedMotion = () =>
typeof window !== 'undefined' &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Safe animate wrapper
export const safeAnimate = (
targets: anime.AnimeParams['targets'],
params: Omit<anime.AnimeParams, 'targets'>
) => {
if (prefersReducedMotion()) {
return anime({ targets, ...params, duration: 0 });
}
return anime({ targets, ...params });
};
// Staggered list entrance
export const staggerIn = (selector: string, delay = 50) =>
safeAnimate(selector, {
translateY: [20, 0],
opacity: [0, 1],
delay: anime.stagger(delay),
duration: 400,
easing: 'easeOutQuad',
});
// Modal entrance
export const modalIn = (selector: string) =>
safeAnimate(selector, {
scale: [0.95, 1],
opacity: [0, 1],
duration: 200,
easing: 'easeOutQuad',
});
// Button press feedback
export const buttonPress = (element: HTMLElement) =>
safeAnimate(element, {
scale: [1, 0.97, 1],
duration: 150,
easing: 'easeInOutQuad',
});
// Success animation
export const successPop = (selector: string) =>
safeAnimate(selector, {
scale: [0, 1.1, 1],
opacity: [0, 1],
duration: 400,
easing: 'easeOutBack',
});
// Error shake
export const errorShake = (selector: string) =>
safeAnimate(selector, {
translateX: [0, -8, 8, -8, 8, -4, 4, 0],
duration: 400,
easing: 'easeInOutQuad',
});
// Fade out (for exits)
export const fadeOut = (selector: string) =>
safeAnimate(selector, {
opacity: [1, 0],
translateY: [0, -10],
duration: 200,
easing: 'easeInQuad',
});
// Number counter
export const countUp = (
element: HTMLElement,
endValue: number,
duration = 1000
) => {
const obj = { value: 0 };
return anime({
targets: obj,
value: endValue,
round: 1,
duration,
easing: 'easeOutExpo',
update: () => {
element.textContent = obj.value.toLocaleString();
},
});
};
// Skeleton pulse
export const skeletonPulse = (selector: string) =>
safeAnimate(selector, {
opacity: [0.5, 1, 0.5],
duration: 1500,
loop: true,
easing: 'easeInOutSine',
});
阶段 4:React Hook 模式
为 React 项目创建可复用的 Hook:
// src/hooks/useAnimation.ts
import { useEffect, useRef, useCallback } from 'react';
import anime from 'animejs';
import { prefersReducedMotion } from '@/lib/animations';
export function useStaggeredEntrance<T extends HTMLElement>(
deps: unknown[] = []
) {
const containerRef = useRef<T>(null);
useEffect(() => {
if (!containerRef.current || prefersReducedMotion()) return;
const children = containerRef.current.children;
anime({
targets: children,
translateY: [20, 0],
opacity: [0, 1],
delay: anime.stagger(50),
duration: 400,
easing: 'easeOutQuad',
});
}, deps);
return containerRef;
}
export function useButtonFeedback() {
const handlePress = useCallback((e: React.MouseEvent<HTMLElement>) => {
if (prefersReducedMotion()) return;
anime({
targets: e.currentTarget,
scale: [1, 0.97, 1],
duration: 150,
easing: 'easeInOutQuad',
});
}, []);
return { onMouseDown: handlePress };
}
export function useHoverLift<T extends HTMLElement>() {
const ref = useRef<T>(null);
useEffect(() => {
const el = ref.current;
if (!el || prefersReducedMotion()) return;
const enter = () => {
anime.remove(el);
anime({
targets: el,
translateY: -4,
boxShadow: '0 8px 30px rgba(0,0,0,0.12)',
duration: 200,
easing: 'easeOutQuad',
});
};
const leave = () => {
anime.remove(el);
anime({
targets: el,
translateY: 0,
boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
duration: 200,
easing: 'easeOutQuad',
});
};
el.addEventListener('mouseenter', enter);
el.addEventListener('mouseleave', leave);
return () => {
el.removeEventListener('mouseenter', enter);
el.removeEventListener('mouseleave', leave);
};
}, []);
return ref;
}
export function useCountUp(endValue: number, duration = 1000) {
const ref = useRef<HTMLElement>(null);
useEffect(() => {
if (!ref.current) return;
const obj = { value: 0 };
anime({
targets: obj,
value: endValue,
round: 1,
duration: prefersReducedMotion() ? 0 : duration,
easing: 'easeOutExpo',
update: () => {
if (ref.current) {
ref.current.textContent = obj.value.toLocaleString();
}
},
});
}, [endValue, duration]);
return ref;
}
阶段 5:实现检查清单
系统地处理各个组件。对于每个组件:
- 识别动画机会(入场、交互、反馈)
- 添加必要的导入
- 使用工具/Hook 实现
- 测试减少动态效果的行为
- 验证 60fps 性能
实现顺序:
- 动画工具文件
- React Hook(如果是 React 项目)
- P0:页面过渡、加载状态
- P1:列表、模态框、表单反馈
- P2:按钮、卡片
- P3:图标、工具提示
阶段 6:常见实现模式
带交错入场效果的列表组件:
function ItemList({ items }) {
const listRef = useStaggeredEntrance<HTMLUListElement>([items]);
return (
<ul ref={listRef}>
{items.map(item => (
<li key={item.id} style={{ opacity: 0 }}>
{item.name}
</li>
))}
</ul>
);
}
带按压反馈的按钮:
function AnimatedButton({ children, onClick, ...props }) {
const feedbackProps = useButtonFeedback();
return (
<button {...props} {...feedbackProps} onClick={onClick}>
{children}
</button>
);
}
带悬停抬升效果的卡片:
function Card({ children }) {
const cardRef = useHoverLift<HTMLDivElement>();
return (
<div ref={cardRef} className="card">
{children}
</div>
);
}
带入场动画的模态框:
function Modal({ isOpen, children }) {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen && modalRef.current) {
modalIn(modalRef.current);
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div ref={modalRef} className="modal" style={{ opacity: 0 }}>
{children}
</div>
);
}
性能规则
始终使用动画(由 GPU 加速):
translateX、translateY、translateZscale、rotateopacity
绝不使用动画(会触发重新布局):
width、heightpadding、margintop、left、right、bottom
时长指南:
| 类型 | 时长 | 缓动 |
|---|---|---|
| 微交互 | 100-200ms | easeOutQuad |
| 按钮反馈 | 150ms | easeInOutQuad |
| 模态框入场 | 200-250ms | easeOutQuad |
| 列表交错 | 间隔 50-100ms | easeOutQuad |
| 页面过渡 | 300ms | easeOutQuint |
输出格式
实现完成后,提供摘要:
## Animation Upgrade Summary
### Files Created
- `src/lib/animations.ts` - Core utilities
- `src/hooks/useAnimation.ts` - React hooks
### Components Updated
| Component | Animation Added | Priority |
|-----------|-----------------|----------|
| ItemList | Staggered entrance | P1 |
| Modal | Scale + fade in | P1 |
| Button | Press feedback | P2 |
### Performance Notes
- All animations use transform/opacity only
- Reduced motion respected globally
- Average animation duration: 200ms
参考文件
详细的 API 文档:references/api-reference.md
更多模式:references/patterns.md
评论
支持 Markdown