关于 animejs-v4
面向 Web Components 开发者的 Anime.js v4 动画参考技能,介绍元素、时间线、弹簧、拖拽及 SVG 动画 API。内容还涵盖组件生命周期中的动画清理和常见交互动画模式。
主要功能
- 01元素属性动画与关键帧
- 02时间线编排与回放控制
- 03错开动画与弹簧缓动
- 04拖拽及滚动驱动动画
- 05SVG 路径与文本动画
适用场景
- 01为组件添加进入和离场效果
- 02编排多个元素的连续动画
- 03制作卡片拖放与交换反馈
- 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
| V3 | V4 |
|---|---|
anime({ targets: '.el' }) | animate('.el', {...}) |
easing: 'easeOutQuad' | ease: 'outQuad' |
direction: 'alternate' | alternate: true |
translateX: 100 | x: 100 |
anime.stagger(100) | stagger(100) |
.finished | .then() 或 await |
评论
支持 Markdown