算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
frontend-animation【设计与创意】Use this skill when the user says 'animation', 'motion', 'Framer Motion', 'GSAP', 'CSS animation', 'page transition', 'enter animation', 'exit animation', 'gesture animation', 'spring animation', 'keyframe', 'motion design'. Delivers animation strategies and code for web applications. Do NOT use for: backend animation or video processing.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Description: Implements web animations -- page transitions, gesture animations, micro-interactions, motion design. Triggered by "animation", "motion", "Framer Motion", "GSAP", "CSS animation", "page transition", "enter animation", "exit animation", "gesture animation", "spring animation", "keyframe", "motion design".
Version: 1.2.0 Author: j4flmao License: MIT
Deliver performant, accessible, and cohesive motion design across the frontend -- from micro-interactions to full page transitions -- while respecting user accessibility preferences and maintaining 60fps frame budget across mid-range devices.
User request includes any of: "animation", "motion", "Framer Motion", "GSAP", "CSS animation", "page transition", "enter animation", "exit animation", "gesture animation", "spring animation", "keyframe", "motion design".
Animation strategy as text / animation code snippets.
## Strategy
<animation-strategy>
## Implementation
<code-snippets>
## Performance Notes
<gpu-compositing, frame-budget notes>
## Accessibility
<reduced-motion handling>
--
Compression footer: frontend-animation/v1 | 4 sections | lib: <selected> | perf: <ok|warn>
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output.
4096 tokens
Is it a React project?
|-- YES --> Is animation declarative (mount/unmount)?
| |-- YES --> Use Framer Motion (AnimatePresence, layout animations)
| |-- NO --> Is it a single gesture or hover?
| |-- YES --> CSS transitions (no JS dep needed)
| |-- NO --> Framer Motion or react-spring
|-- NO --> Is it a complex timeline sequence?
|-- YES --> GSAP (timelines, scrollTrigger, SVG morph)
|-- NO --> Is it a simple CSS animation?
|-- YES --> CSS @keyframes + transitions
|-- NO --> WAAPI (Web Animations API)
Option A: CSS-only animation -- Zero JS overhead. Best for: hover states, focus rings, loading spinners, skeleton pulses. Limited to simple transitions and keyframes. No timeline sequencing. Control via CSS custom properties for theming.
.element {
transition: transform 200ms ease, opacity 200ms ease;
}
.element:hover {
transform: scale(1.05);
}
Option B: Framer Motion (React) -- Declarative spring animations, layout animations via layout prop, exit animations via AnimatePresence, gesture handling (drag, swipe, hover, tap). Best for: component-level animations in React SPAs and Next.js. Bundle size: ~35KB gzipped.
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 25 }}
/>
Option C: GSAP (GreenSock) -- Professional-grade timeline control, ScrollTrigger plugin, SVG morphing, cross-browser consistency. Best for: marketing pages, complex reveal sequences, scroll-driven narratives, non-React projects. Bundle size: ~45KB gzipped with ScrollTrigger.
Option D: WAAPI (Web Animations API) -- Native browser API. Framework-agnostic. Zero dependency cost. Best for: simple imperative sequences, polyfill-free modern browsers. Limited easing options (no spring), no timeline chaining without manual orchestration.
element.animate([
{ transform: 'scale(1)', opacity: 1 },
{ transform: 'scale(1.1)', opacity: 0.8 }
], { duration: 200, easing: 'ease-out', fill: 'forwards' })
| Criterion | CSS | Framer Motion | GSAP | WAAPI |
|---|---|---|---|---|
| Bundle cost | 0 KB | ~35 KB | ~45 KB | 0 KB |
| Spring physics | No | Yes (configurable) | Yes (GSAP 3.6+) | No |
| Timeline sequencing | Manual | AnimatePresence | Native timeline | Manual promise chain |
| Scroll-driven | Intersection Observer | useInView | ScrollTrigger | Intersection Observer |
| SVG morphing | No | layout animation | MorphSVG plugin | No |
| Cross-browser | Native | React only | All (IE11+) | Modern browsers only |
| Learning curve | Low | Medium | Medium-High | Low |
AnimatePresence (Framer Motion) or manual mount/unmount with CSS for exit animations.layout prop (Framer Motion) or FLIP technique (GSAP Flip).layoutId (Framer Motion) or record element positions before/after DOM change.layout prop on page components with AnimatePresence in _app.tsx or use the page transition API.AnimatePresence mode="wait" for sequential enter/exit.dragConstraints.onDragEnd with velocity extraction.whileHover for interactive elements.whileTap.transform and opacity.width, height, top, left, margin, padding -- cause layout thrashing.will-change: transform on persistently animated elements (remove after idle).contain: layout style paint on non-animated parents.prefers-reduced-motion: reduce -- instant state transitions (0ms duration, no parallax, no auto-play).prefers-reduced-motion: no-preference -- full animations.matchMedia('(prefers-reduced-motion: reduce)') for JS gating.grid-template-rows: 0fr to 1fr with CSS transition (no JS height calc).useInView hook for React projects.transition.staggerChildren (Framer Motion) or GSAP timeline offset.motion.path pathLength for draw effects.motion mocked to div for unit tests.page.waitForSelector for animation end states.prefers-reduced-motion: reduce and verify state changes still communicate.Using width, height, top, left triggers layout recalculation on every frame. This causes jank, especially on mobile. Always use transform: scale() for size and transform: translate() for position.
/* BAD */
.element {
animation: slide 300ms;
}
@keyframes slide {
from { left: -100px; }
to { left: 0; }
}
/* GOOD */
@keyframes slide {
from { transform: translateX(-100px); }
to { transform: translateX(0); }
}
When removing elements from the DOM, the element disappears instantly unless you explicitly animate exit. In React, wrap with AnimatePresence and define exit prop. For CSS, use a timeout to toggle visibility before removal.
Default spring parameters often cause visible bounce that feels unpolished. For UI elements, use stiffness: 200-300 and damping: 15-25. Stiffness > 500 creates robotic motion. Damping < 10 causes excessive wobble.
Attaching animation triggers to scroll without throttling fires hundreds of callbacks per second. Use requestAnimationFrame or IntersectionObserver instead of scroll events. If scroll events are unavoidable, throttle to 16ms intervals.
More than 5-8 simultaneous animated elements will drop frames on mid-range devices. Batch animations into staggered sequences. Use will-change sparingly -- each promoted layer consumes GPU memory.
Auto-playing animations (carousels, marquees, background video) that ignore prefers-reduced-motion cause vestibular disorders for users with motion sensitivity. Always gate auto-play animations behind the reduced motion media query.
When animating elements out of the document flow, surrounding content snaps to fill the gap before the animation completes. Use position: absolute on exiting elements or animate height alongside opacity with overflow: hidden on the parent.
fill: forwards on Final FrameWithout animation-fill-mode: forwards or WAAPI fill: "forwards", the animated element snaps back to its pre-animation state when the animation ends. This creates a jarring visual pop.
| Approach | Strengths | Weaknesses | Best Use Case |
|---|---|---|---|
| CSS transitions/keyframes | Zero JS, GPU-composited, declarative | No timeline, no spring, limited sequencing | Hover, focus, simple state changes |
| Framer Motion | Declarative springs, layout animations, exit animations, drag/gesture | React-only, ~35KB bundle | React component animations |
| GSAP | Full timeline control, ScrollTrigger, SVG morph, IE11+ | ~45KB, commercial license for some plugins | Marketing sites, complex sequences |
| react-spring | Physics-based springs, interpolation | Smaller ecosystem, fewer gesture helpers | Natural-feeling UI motion |
| WAAPI | Zero cost, native, imperative | No spring easing, no timeline chaining | Simple imperative sequences |
| Lottie/Bodymovin | After Effects export, complex vector animations | Large file sizes, no runtime interaction | Brand animations, splash screens |
| anime.js | Lightweight, versatile JS animations | Smaller community, less maintenance | General purpose JS animations |
// Framer Motion — complex gesture composition
import { motion, useMotionValue, useTransform, useSpring } from 'framer-motion';
function Card3D({ children }: { children: React.ReactNode }) {
const x = useMotionValue(0);
const y = useMotionValue(0);
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [10, -10]), {
stiffness: 300, damping: 30,
});
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-10, 10]), {
stiffness: 300, damping: 30,
});
function handleMouseMove(event: React.MouseEvent) {
const rect = event.currentTarget.getBoundingClientRect();
const xPos = (event.clientX - rect.left) / rect.width - 0.5;
const yPos = (event.clientY - rect.top) / rect.height - 0.5;
x.set(xPos); y.set(yPos);
}
function handleMouseLeave() { x.set(0); y.set(0); }
return (
<motion.div onMouseMove={handleMouseMove} onMouseLeave={handleMouseLeave}
style={{ rotateX, rotateY, transformStyle: 'preserve-3d' }}>
{children}
</motion.div>
);
}
| Feel | Stiffness | Damping | Mass | Use Case |
|---|---|---|---|---|
| Snappy UI | 300 | 25 | 0.5 | Buttons, toggles, micro-interactions |
| Gentle UI | 200 | 20 | 1.0 | Cards, modals, sheets |
| Bouncy | 150 | 10 | 1.0 | Fun reveal, celebration animations |
| Heavy | 500 | 40 | 2.0 | Drag constraints, large elements |
| Fluid | 100 | 15 | 1.0 | Page transitions, parallax |
| Stiff (no bounce) | 400 | 40 | 1.0 | Progress bars, loading indicators |
const uiSpring = { type: 'spring', stiffness: 300, damping: 20, mass: 0.5 };
const gestureSpring = { type: 'spring', stiffness: 500, damping: 30, mass: 1 };
function ReorderList() {
const [items, setItems] = useState(['A', 'B', 'C', 'D']);
return (
<div>
<button onClick={() => setItems([...items].sort(() => Math.random() - 0.5))}>Shuffle</button>
<ul>
{items.map((item) => (
<motion.li key={item} layout transition={{ type: 'spring', stiffness: 300, damping: 25 }}>
{item}
</motion.li>
))}
</ul>
</div>
);
}
// Shared layoutId enables cross-page element transitions
// Page A: <motion.img layoutId={`product-image-${id}`} src={product.image} />
// Page B: <motion.img layoutId={`product-image-${id}`} src={product.image} style={{ width: '100%', height: 400 }} />
// Framer Motion animates the transition between the two layouts automatically
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out both;
}
.animate-stagger > * {
opacity: 0;
animation: fadeIn 0.3s ease-out both;
}
.animate-stagger > *:nth-child(1) { animation-delay: 0ms; }
.animate-stagger > *:nth-child(2) { animation-delay: 75ms; }
.animate-stagger > *:nth-child(3) { animation-delay: 150ms; }
.animate-stagger > *:nth-child(4) { animation-delay: 225ms; }
// View Transition API (Chrome 111+) — instant cross-page morphing
// app/layout.tsx
function navigateWithTransition(path: string) {
if (document.startViewTransition) {
document.startViewTransition(() => {
router.push(path);
});
} else {
router.push(path);
}
}
// Set view-transition-name on shared elements
// <img style={{ viewTransitionName: 'product-image' }} />
| Animation Type | Max Elements | Max Duration | Frame Budget |
|---|---|---|---|
| Micro-interaction (hover, tap) | 1-2 | 300ms | < 4ms JS |
| Page transition | 5-10 | 400ms | < 8ms JS |
| Scroll reveal | 10-20 | 600ms | < 6ms JS |
| Parallax | 3-5 | Continuous | < 2ms JS |
| Particle (DOM) | 5-8 | 2s | < 8ms JS |
| Particle (Canvas) | 100-1000 | 5s | < 10ms JS |
Total budget per frame at 60fps: 16.67ms. Recommended allocation: JS animation work < 8ms, style recalculation < 3ms, paint + composite < 5ms. Exceeding 16ms causes dropped frames.
The browser promotes elements to GPU compositor layers when will-change, 3D transforms (translateZ(0)), or <video>/<canvas> are present. Each layer consumes ~1-2MB of GPU memory. On mobile devices with 128-256MB GPU memory, 50+ layers exhaust memory and crash the GPU process.
performance.now() markers around animation callbacks: log durationsrequestAnimationFrame callback timestamps: monitor frame spacing (delta > 20ms indicates jank)will-change is purely cosmetic — does not introduce security concernsscrollTrigger does not introduce XSS vectors when used with static selectorsprefers-reduced-motion: use @media (prefers-reduced-motion: reduce) to disable or simplify animationsMotionConfig reducedMotion="user" auto-handles the media querygsap.ticker.lagSmoothing(0) and manually check window.matchMedia('(prefers-reduced-motion: reduce)'):focus-visible styles must be instanttransform and opacity. Never animate width, height, top, left, margin, padding.prefers-reduced-motion -- instant transitions on reduce, full motion on no-preference.will-change sparingly -- only on elements that animate continuously, and remove when idle.prefers-reduced-motion.transform-origin correctly for scale/rotate animations -- default is center.box-shadow -- prefer pseudo-element opacity trick for performance.scrub: 1 or toggleActions for smooth scroll-linked motion.lotti-web.references/animation-accessibility.md -- Animation Accessibilityreferences/animation-anatomy.md -- Animation Anatomyreferences/animation-libraries.md -- Animation Librariesreferences/animation-performance.md -- Animation Performancereferences/animation-techniques.md -- Animation Techniquesreferences/animation-tools.md -- Animation Tools Referencereferences/web-animation-performance.md -- Web Animation Performance & Compositing Deep Divereferences/spring-gesture-animation.md -- Spring Physics & Gesture Animation PatternsWhen complete, output the animation strategy with implementation snippets. If the request scope exceeds page transitions + micro-interactions (e.g., full motion design system), flag for a dedicated motion designer handoff. For GSAP ScrollTrigger-heavy work, flag for scroll experience specialist.