子元素错开动画
子元素依次错开执行入场动画,通过延迟差营造流畅的序列感。
交互预览
动效定义与代码
{
"slug": "stagger-children",
"name": "子元素错开动画",
"nameEn": "Stagger Children",
"description": "子元素依次错开执行入场动画,通过延迟差营造流畅的序列感。",
"descriptionEn": "Child elements animate in sequence with staggered delays, creating a smooth cascading effect.",
"category": "micro-interaction",
"tags": [
"stagger",
"sequence",
"children",
"cascade",
"list"
],
"trigger": "on-mount",
"difficulty": "intermediate",
"duration": "500ms",
"easing": "cubic-bezier(0.16, 1, 0.3, 1)",
"cssProperties": [
"opacity",
"transform"
],
"isGPUAccelerated": true,
"previewBg": "light",
"keywords": [
"stagger",
"cascade",
"sequence",
"children",
"list animation",
"delay"
],
"useCases": [
"List item entrance",
"Grid card reveal",
"Navigation menu items",
"Dashboard widget loading"
],
"relatedAnimations": [
"fade-in-up",
"scroll-reveal"
],
"recommendedStyles": [
"bento-grid",
"masonry-flow",
"card-stack"
],
"codeSnippets": [
{
"label": "CSS Keyframes",
"language": "css",
"code": "@keyframes stagger-fade-in {\n from {\n opacity: 0;\n transform: translateY(15px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.stagger-children > * {\n will-change: transform, opacity;\n opacity: 0;\n animation: stagger-fade-in 500ms cubic-bezier(0.16, 1, 0.3, 1) both;\n animation-delay: calc(var(--stagger-index, 0) * var(--stagger-delay, 75ms));\n}\n\n/* Usage: set --stagger-index on each child via inline style\n <div class=\"stagger-children\">\n <div style=\"--stagger-index: 0\">...</div>\n <div style=\"--stagger-index: 1\">...</div>\n <div style=\"--stagger-index: 2\">...</div>\n </div>\n Override delay: style=\"--stagger-delay: 100ms\" on parent */\n\n@media (prefers-reduced-motion: reduce) {\n .stagger-children {\n animation: none;\n opacity: 1;\n transform: none;\n }\n}"
},
{
"label": "Tailwind CSS",
"language": "tsx",
"code": "// React + Tailwind approach with inline delay\nfunction StaggerList({ items }: { items: string[] }) {\n return (\n <ul className=\"space-y-2\">\n {items.map((item, i) => (\n <li\n key={item}\n className=\"animate-fade-in-up opacity-0\"\n style={{ animationDelay: `${i * 75}ms`, animationFillMode: \"both\" }}\n >\n {item}\n </li>\n ))}\n </ul>\n );\n}\n\n/* Requires this in your CSS: */\n// @keyframes fade-in-up {\n// from { opacity: 0; transform: translateY(15px); }\n// to { opacity: 1; transform: translateY(0); }\n// }\n// @utility animate-fade-in-up {\n// animation: fade-in-up 500ms cubic-bezier(0.16, 1, 0.3, 1);\n// }"
},
{
"label": "Framer Motion",
"language": "tsx",
"code": "import { motion } from \"framer-motion\";\n\n// Tip: respect user's motion preferences\n// import { useReducedMotion } from \"framer-motion\";\n// const reduced = useReducedMotion();\n// Use reduced ? { opacity: 0 } : { opacity: 0, y: 15 } for initial\n\nconst container = {\n hidden: { opacity: 0 },\n show: {\n opacity: 1,\n transition: { staggerChildren: 0.075 },\n },\n};\n\nconst item = {\n hidden: { opacity: 0, y: 15 },\n show: { opacity: 1, y: 0 },\n};\n\n<motion.ul variants={container} initial=\"hidden\" animate=\"show\">\n {items.map((text) => (\n <motion.li key={text} variants={item}>\n {text}\n </motion.li>\n ))}\n</motion.ul>"
}
]
}