mcpField
A
animejs-mastery

animejs-mastery

github.com/frogody设计与 UI45 次安装
代码仓库

关于 animejs-mastery

animejs-mastery 是一项自主 Agent 技能,可分析代码库并使用 anime.js 实现界面动画。它涵盖机会识别、可复用工具与 React hooks,并检查性能和减少动态效果偏好。

主要功能

  1. 01分析代码库和框架
  2. 02扫描动画应用机会
  3. 03创建共享动画工具
  4. 04编写可复用的 React hooks
  5. 05检查减少动态效果和性能

适用场景

  1. 01添加页面切换和加载动画
  2. 02为列表、模态框和表单反馈添加动画
  3. 03制作按钮和卡片交互效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/frogody/app.isyncso --skill animejs-mastery

安装后在 Claude Code 中输入/animejs-mastery确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-reference.md
patterns.md
scripts/
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:实现检查清单

系统地处理各个组件。对于每个组件:

  1. 识别动画机会(入场、交互、反馈)
  2. 添加必要的导入
  3. 使用工具/Hook 实现
  4. 测试减少动态效果的行为
  5. 验证 60fps 性能

实现顺序:

  1. 动画工具文件
  2. React Hook(如果是 React 项目)
  3. P0:页面过渡、加载状态
  4. P1:列表、模态框、表单反馈
  5. P2:按钮、卡片
  6. 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、translateZ
  • scale、rotate
  • opacity

绝不使用动画(会触发重新布局):

  • width、height
  • padding、margin
  • top、left、right、bottom

时长指南:

类型时长缓动
微交互100-200mseaseOutQuad
按钮反馈150mseaseInOutQuad
模态框入场200-250mseaseOutQuad
列表交错间隔 50-100mseaseOutQuad
页面过渡300mseaseOutQuint

输出格式

实现完成后,提供摘要:

## 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