Skip to content

Commit a67fdf2

Browse files
committed
feat(ui): add CSS stagger entrance animation for page content sections
- Add stagger-fade-in CSS classes and fade-in-up keyframes to performance.css. Pure CSS animation (zero JS overhead, runs on compositor thread). Supports up to 8 children with 60ms stagger intervals, each child fades in and slides up 12px. - Apply stagger-fade-in to TodayView's outer container, replacing Framer Motion staggerContainer + fadeInUp variants. The page's 7 sections (Planner, Todos, QuickActions, Stats, Goals, Social, KnowledgePreview) now cascade in smoothly. - Remove unused Framer Motion variants (staggerContainer, heroStatVariant, fadeInUp) and SPRING import. - Retain motion.button/motion.div for interactive hover/tap effects (whileHover, whileTap) where gesture response is needed.
1 parent 3d3a7f7 commit a67fdf2

2 files changed

Lines changed: 30 additions & 35 deletions

File tree

‎client/src/features/dashboard/components/views/TodayView.tsx‎

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { useNavigate } from 'react-router-dom';
1010
import { motion } from 'framer-motion';
1111
import { Timer, FileText, Layers, Lightbulb } from 'lucide-react';
1212
import { cn } from '@/lib/utils';
13-
import { SPRING, fadeInUp } from '@/lib/animation/springConfig';
1413
import { useReducedMotion } from '@/hooks/useReducedMotion';
1514
import ModuleRitualHeader from '@/components/ui/ModuleRitualHeader';
1615
import { DashboardCard } from '../DashboardCard';
@@ -26,16 +25,6 @@ const accentText: Record<string, string> = {
2625
flashcard: 'text-flashcard', feynman: 'text-feynman',
2726
};
2827

29-
const staggerContainer = {
30-
hidden: {},
31-
visible: { transition: { staggerChildren: 0.08, delayChildren: 0.1 } },
32-
};
33-
34-
const heroStatVariant = {
35-
hidden: { opacity: 0, y: 20, scale: 0.95 },
36-
visible: { opacity: 1, y: 0, scale: 1, transition: SPRING.gentle },
37-
};
38-
3928
const quickActions = [
4029
{ label: '专注', icon: Timer, path: '/pomodoro', accent: 'pomodoro' as const },
4130
{ label: '笔记', icon: FileText, path: '/notes', accent: 'note' as const },
@@ -76,12 +65,10 @@ export function TodayView({
7665
];
7766

7867
return (
79-
<div>
68+
<div className="stagger-fade-in">
8069
{/* ════ 今日航线:行动导向主角 ════ */}
8170
<section className="relative max-w-[1100px] mx-auto px-6 pt-rhythm-md">
82-
<motion.div {...fadeInUp}>
83-
<PlannerPanel />
84-
</motion.div>
71+
<PlannerPanel />
8572
</section>
8673

8774
{/* ════ 今日待办:未完成显性化(蔡格尼克效应) ════ */}
@@ -128,7 +115,7 @@ export function TodayView({
128115

129116
{/* ════ 快捷操作 ════ */}
130117
<section className="relative max-w-[1100px] mx-auto px-6 mt-rhythm-md">
131-
<motion.div className="flex gap-3" variants={staggerContainer}>
118+
<div className="flex gap-3">
132119
{quickActions.map((action) => {
133120
const Icon = action.icon;
134121
return (
@@ -142,31 +129,27 @@ export function TodayView({
142129
'text-b3 font-medium text-text-secondary hover:text-text-primary',
143130
'transition-all duration-beat-x2',
144131
)}
145-
variants={heroStatVariant}
146132
whileHover={{ y: -2 }}
147133
whileTap={{ scale: 0.96 }}
148-
transition={SPRING.default}
149134
>
150135
<Icon className={cn('w-4 h-4', accentText[action.accent])} strokeWidth={1.5} />
151136
{action.label}
152137
</motion.button>
153138
);
154139
})}
155-
</motion.div>
140+
</div>
156141
</section>
157142

158143
{/* ════ 今日统计:状态反馈 ════ */}
159144
<section className="relative max-w-[1100px] mx-auto px-6 mt-rhythm-md">
160-
<motion.div className="grid grid-cols-2 lg:grid-cols-4 gap-rhythm-sm" variants={staggerContainer}>
145+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-rhythm-sm">
161146
{heroStats.map((stat) => {
162147
const Icon = stat.icon;
163148
return (
164149
<motion.div
165150
key={stat.label}
166-
variants={heroStatVariant}
167151
whileHover={{ y: -2, scale: 1.02 }}
168152
whileTap={{ scale: 0.97 }}
169-
transition={SPRING.default}
170153
>
171154
<DashboardCard
172155
accent={stat.accent}
@@ -195,7 +178,7 @@ export function TodayView({
195178
</motion.div>
196179
);
197180
})}
198-
</motion.div>
181+
</div>
199182
</section>
200183

201184
{/* 🎯 目标进度(目标梯度效应,紧邻计划区) */}
@@ -212,15 +195,15 @@ export function TodayView({
212195

213196
{/* ════ 知识预览:回到学习现场 ════ */}
214197
<section className="relative max-w-[1100px] mx-auto px-6 mt-rhythm-lg pb-rhythm-xl">
215-
<motion.div className="mb-rhythm-sm" {...fadeInUp}>
198+
<div className="mb-rhythm-sm">
216199
<ModuleRitualHeader
217200
title="知识预览"
218201
note="最近的学习足迹"
219202
sealChar="星"
220203
sealColor="#40AB92"
221204
compact
222205
/>
223-
</motion.div>
206+
</div>
224207

225208
{isLoading ? (
226209
<div className="grid grid-cols-2 lg:grid-cols-3 gap-rhythm-sm">
@@ -233,18 +216,11 @@ export function TodayView({
233216
))}
234217
</div>
235218
) : knowledgeCards.length > 0 ? (
236-
<motion.div
237-
className="grid grid-cols-2 lg:grid-cols-3 gap-rhythm-sm"
238-
variants={staggerContainer}
239-
initial="hidden"
240-
animate="visible"
241-
>
219+
<div className="grid grid-cols-2 lg:grid-cols-3 gap-rhythm-sm">
242220
{knowledgeCards.map((card, i) => (
243-
<motion.div key={card.id} variants={heroStatVariant}>
244-
<KnowledgePreviewCard card={card} index={i} />
245-
</motion.div>
221+
<KnowledgePreviewCard key={card.id} card={card} index={i} />
246222
))}
247-
</motion.div>
223+
</div>
248224
) : (
249225
<div className="text-center py-rhythm-xl">
250226
<p className="text-b2 text-text-tertiary">

‎client/src/styles/performance.css‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,22 @@
4848
opacity: 0;
4949
}
5050
}
51+
52+
/* ── 页面内容渐入动画(stagger) ── */
53+
/* 适用于页面多个 section 的渐入出场,子元素按顺序渐入 */
54+
.stagger-fade-in > * {
55+
animation: fade-in-up 0.35s ease-out both;
56+
}
57+
.stagger-fade-in > :nth-child(1) { animation-delay: 0ms; }
58+
.stagger-fade-in > :nth-child(2) { animation-delay: 60ms; }
59+
.stagger-fade-in > :nth-child(3) { animation-delay: 120ms; }
60+
.stagger-fade-in > :nth-child(4) { animation-delay: 180ms; }
61+
.stagger-fade-in > :nth-child(5) { animation-delay: 240ms; }
62+
.stagger-fade-in > :nth-child(6) { animation-delay: 300ms; }
63+
.stagger-fade-in > :nth-child(7) { animation-delay: 360ms; }
64+
.stagger-fade-in > :nth-child(8) { animation-delay: 420ms; }
65+
66+
@keyframes fade-in-up {
67+
from { opacity: 0; transform: translateY(12px); }
68+
to { opacity: 1; transform: translateY(0); }
69+
}

0 commit comments

Comments
 (0)