mcpField
A
animejs-v4

animejs-v4

github.com/matthewharwood设计与 UI37 次安装
代码仓库

关于 animejs-v4

面向 Web Components 开发者的 Anime.js v4 动画参考技能,介绍元素、时间线、弹簧、拖拽及 SVG 动画 API。内容还涵盖组件生命周期中的动画清理和常见交互动画模式。

主要功能

  1. 01元素属性动画与关键帧
  2. 02时间线编排与回放控制
  3. 03错开动画与弹簧缓动
  4. 04拖拽及滚动驱动动画
  5. 05SVG 路径与文本动画

适用场景

  1. 01为组件添加进入和离场效果
  2. 02编排多个元素的连续动画
  3. 03制作卡片拖放与交换反馈
  4. 04创建随滚动触发的 SVG 动画

安装

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

$npx skills add https://github.com/matthewharwood/fantasy-phonics --skill animejs-v4

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

详细介绍

面向 Web Components 的 Anime.js 4.0

安装

npm i animejs
import { animate, createTimeline, stagger, createSpring, createDraggable } from 'animejs';
import { createDrawable, createMotionPath, morphTo, splitText, onScroll } from 'animejs';

核心 API

基本动画
animate('.el', {
  x: 250, y: 100, rotate: 90, scale: 1.5, opacity: 0.5,
  backgroundColor: '#FFF',
  duration: 1000, delay: 200, ease: 'outQuad',
  loop: true, alternate: true
});
属性级控制
animate('.el', {
  x: { from: -100, to: 100, duration: 800 },
  rotate: { from: 0, to: 360, ease: 'inOutCirc' },
  scale: { to: 1.5, delay: 200 }
});
关键帧
animate('.el', { y: [0, -50, 0], scale: [1, 1.2, 1], duration: 1000 });
交错动画
animate('.items', {
  y: -20, opacity: [0, 1],
  delay: stagger(100),                     // Sequential
  delay: stagger(100, { from: 'center' }), // From center
  delay: stagger(50, { grid: [5, 5] })     // Grid layout
});
基于函数的值
animate('.items', {
  x: (el, i, total) => i * 50,
  rotate: (el, i) => i % 2 === 0 ? 45 : -45
});

播放控制

const anim = animate('.el', { x: 100, autoplay: false });

anim.play();
anim.pause();
anim.reverse();
anim.restart();
anim.seek(500);      // Seek to 500ms
anim.seek('50%');    // Seek to 50%
方法行为
cancel()立即停止,保留当前内联样式
revert()停止并移除所有内联样式
complete()立即跳转到最终值

回调与 Promise

animate('.el', {
  x: 100,
  onBegin: (anim) => {},
  onUpdate: (anim) => {},
  onComplete: (anim) => {}
});

await animate('.el', { x: 100 });  // Promise-based

时间线

const tl = createTimeline({ loop: true, alternate: true });
tl.add('.box1', { x: 100 })
  .add('.box2', { y: 50 }, '-=200')    // 200ms before previous ends
  .add('.box3', { scale: 2 }, '+=100') // 100ms after previous
  .add('.box4', { rotate: 90 }, 500);  // At 500ms absolute

tl.label('myLabel', 1000);
tl.call(() => console.log('done'), 2000);

缓动与弹簧效果

内置: linear、in、out、inOut、outIn(带 power:out(3))。 命名: inQuad、outQuad、inOutQuad、inCubic、outExpo、inOutElastic、outBounce、outBack。

// Spring physics
animate('.el', { x: 100, ease: createSpring({ stiffness: 400, damping: 25 }) });
弹簧预设
const springs = {
  click:  createSpring({ stiffness: 600, damping: 30 }),
  move:   createSpring({ stiffness: 300, damping: 25 }),
  drop:   createSpring({ stiffness: 400, damping: 20 }),
  settle: createSpring({ stiffness: 200, damping: 25 }),
  bounce: createSpring({ stiffness: 500, damping: 10 })
};

Web Component 集成

动画管理器模式

启动新动画前,请务必取消已有动画。 使用 WeakMap 跟踪每个元素的动画,避免内存泄漏。

class AnimationManager {
  #anims = new WeakMap();

  animate(el, props, key = 'main') {
    const map = this.#anims.get(el) || {};
    map[key]?.cancel();  // Cancel existing

    const anim = animate(el, props);
    map[key] = anim;
    this.#anims.set(el, map);
    return anim;
  }

  cancel(el, key = 'main') { this.#anims.get(el)?.[key]?.cancel(); }
  revert(el, key = 'main') { this.#anims.get(el)?.[key]?.revert(); }
}
组件生命周期

遵循 web-components-architecture 技能——使用 disconnectedCallback 进行清理:

class AnimatedCard extends HTMLElement {
  #anim = null;

  animate(props) {
    this.#anim?.cancel();
    this.#anim = animate(this, props);
    return this.#anim;
  }

  disconnectedCallback() {
    this.#anim?.revert();  // Clean up inline styles
  }
}

拖放动画

开始拖动(抬起)
function animateDragStart(card) {
  return animate(card, {
    scale: 1.12, rotate: 8,
    boxShadow: '0 20px 50px rgba(0,0,0,0.3)',
    duration: 150, ease: 'out(3)'
  });
}
拖动期间——直接变换(60fps)

请勿使用 animate() 跟踪指针。 直接设置变换属性以实现平滑移动:

function updateDragPosition(el, x, y) {
  el.style.transform = `translate(${x}px, ${y}px) scale(1.12) rotate(8deg)`;
}
放置动画
function animateDrop(card) {
  return animate(card, {
    x: 0, y: 0,
    scale: [1.12, 0.95, 1],
    rotate: [8, -2, 0],
    boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
    duration: 350, ease: 'outBack'
  });
}
返回原位
function animateReturn(card) {
  return animate(card, {
    x: 0, y: 0, scale: 1, rotate: 0,
    duration: 400, ease: 'outBack'
  });
}
卡片交换(FLIP 模式)
async function animateSwap(cardA, cardB, slotA, slotB) {
  const rectA = cardA.getBoundingClientRect();
  const rectB = cardB.getBoundingClientRect();

  // DOM swap first
  slotA.appendChild(cardB);
  slotB.appendChild(cardA);

  const newRectA = cardA.getBoundingClientRect();
  const newRectB = cardB.getBoundingClientRect();

  // Animate from old positions
  await Promise.all([
    animate(cardA, {
      x: [rectA.left - newRectA.left, 0],
      y: [rectA.top - newRectA.top, 0],
      scale: [1.1, 0.95, 1],
      duration: 400, ease: 'outBack'
    }),
    animate(cardB, {
      x: [rectB.left - newRectB.left, 0],
      y: [rectB.top - newRectB.top, 0],
      scale: [1, 1.05, 1],
      duration: 400, ease: 'outBack'
    })
  ]);
}
槽位悬停反馈
function animateSlotHover(slot, isOver) {
  animate(slot, {
    scale: isOver ? 1.03 : 1,
    borderColor: isOver ? '#4CAF50' : '#999',
    duration: 150, ease: 'out(2)'
  });
}

点击动画

// Bounce feedback
animate(card, {
  scale: [1, 1.15, 1],
  rotate: [0, 5, -5, 0],
  duration: 400, ease: 'outElastic(1, 0.5)'
});

// Disabled shake
animate(card, { x: [0, -8, 8, -6, 6, 0], duration: 300, ease: 'linear' });

滚动驱动动画

animate('.el', {
  x: 300,
  autoplay: onScroll({ target: '.el', enter: 'bottom', leave: 'top', sync: true })
});

SVG 动画

animate(createDrawable('path'), { draw: '0 1' });           // Draw path
animate('.el', { ...createMotionPath('.path') });           // Motion path
animate('.shape1', { d: morphTo('.shape2') });              // Morph shapes

文本拆分

const { chars } = splitText('.text', { chars: true });
animate(chars, { y: [20, 0], opacity: [0, 1], delay: stagger(30) });

内置可拖动功能

createDraggable('.el', {
  x: true, y: true, snap: 50, container: '.bounds',
  releaseEase: createSpring({ stiffness: 120, damping: 6 }),
  onDrag: (d) => {}, onRelease: (d) => {}
});

从 V3 迁移到 V4

V3V4
anime({ targets: '.el' })animate('.el', {...})
easing: 'easeOutQuad'ease: 'outQuad'
direction: 'alternate'alternate: true
translateX: 100x: 100
anime.stagger(100)stagger(100)
.finished.then() 或 await

评论

支持 Markdown