loading-states-and-perceived-performance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLoading States and Perceived Performance
加载状态与感知性能
Users don't mind waiting as much if they understand what they are waiting for and how much progress is being made. Perceived performance is the design work of making a system feel faster than it actually is.
如果用户清楚自己在等待什么以及进度如何,他们就不会那么在意等待时间。感知性能是通过设计让系统看起来比实际速度更快的工作。
Choosing the Right Loading State
选择合适的加载状态
| Wait Duration | Best Pattern | Use for |
|---|---|---|
| Short (< 1s) | Inline Spinner / Loader | Button actions, small updates, quick data fetches |
| Medium (1s – 3s) | Skeleton Screen | Cards, lists, dashboards, profile pages |
| Long (> 3s) | Determinate Progress Bar | File uploads, complex exports, heavy processing |
| Full Page | Staggered Entry / Animated Sections | Initial app load, hero sections, immersive transitions |
| 等待时长 | 最佳模式 | 适用场景 |
|---|---|---|
| 短(<1秒) | Inline Spinner / 加载器 | 按钮操作、小型更新、快速数据获取 |
| 中(1秒–3秒) | Skeleton骨架屏 | 卡片、列表、仪表盘、个人资料页 |
| 长(>3秒) | 确定性进度条 | 文件上传、复杂导出、重型处理任务 |
| 整页加载 | 交错入场/动画区块 | 应用初始加载、首页英雄区、沉浸式过渡 |
Simple Cases: Spinners and Loaders
简单场景:Spinner与加载器
Use spinners for small, contained actions where the layout doesn't change significantly.
- Button Spinners: Replace button text or sit alongside it. The button should enter a state to prevent double-submissions.
disabled - Micro-Loaders: A small 16–24px circle for inline updates (e.g., saving a single field).
- Animation Tip: A "spring-loaded" rotation (easing in and out) feels more premium than a constant linear rotation.
css
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
animation: spin 800ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}在布局不会发生显著变化的小型、独立操作中使用Spinner。
- 按钮Spinner: 替换按钮文本或放在文本旁。按钮应进入状态以防止重复提交。
disabled - 微型加载器: 16–24px的小型圆形加载器,用于内联更新(例如,保存单个字段)。
- 动画技巧: “弹簧式”旋转(缓入缓出)比匀速线性旋转更具高级感。
css
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
animation: spin 800ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}Skeleton Screens (Glimmer/Shimmer)
Skeleton骨架屏(微光/闪烁效果)
Skeleton screens provide a visual placeholder that mimics the layout of the final content. This reduces "layout shift" (CLS) and signals to the user exactly where the content will appear.
Skeleton骨架屏提供模仿最终内容布局的视觉占位符。这能减少“布局偏移”(CLS),并向用户明确指示内容将出现的位置。
The Shimmer Effect
闪烁效果
A subtle, moving gradient that travels across the skeleton elements.
css
.skeleton {
background: var(--color-grey-100);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}一种在骨架元素上移动的细微渐变效果。
css
.skeleton {
background: var(--color-grey-100);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}Rules for Skeletons
骨架屏设计规则
- Match the shape: If the final content is a round avatar, use a round skeleton. If it's a 2-line heading, use two bars of varying widths.
- Stay Recessive: Skeletons should use your most subtle grey (or
--color-grey-100). They should not draw focus.grey-50 - Fade into Reality: When data arrives, fade the actual content in over the skeleton (150–200ms) rather than snapping.
- 匹配形状: 如果最终内容是圆形头像,就使用圆形骨架;如果是两行标题,就使用两个不同宽度的条形骨架。
- 保持低调: 骨架应使用最浅的灰色(或
--color-grey-100),不应吸引用户注意力。grey-50 - 平滑过渡到真实内容: 数据加载完成后,让真实内容在骨架上方淡入(150–200毫秒),而非突然切换。
Fully Animated Sections
全动画区块
For major page transitions or initial loads, use a coordinated animation strategy.
对于大型页面过渡或初始加载,采用协调的动画策略。
Staggered Entry (Cascading)
交错入场(级联效果)
Instead of the whole page appearing at once, animate sections in a sequence. This guides the user's eye from the most important content (hero) down to secondary areas.
css
.section {
opacity: 0;
transform: translateY(10px);
animation: slide-up 400ms ease-out forwards;
}
/* Stagger by index */
.section:nth-child(1) { animation-delay: 100ms; }
.section:nth-child(2) { animation-delay: 200ms; }
.section:nth-child(3) { animation-delay: 300ms; }
@keyframes slide-up {
to { opacity: 1; transform: translateY(0); }
}不要让整页内容同时出现,而是按顺序为各个区块添加动画。这能引导用户视线从最重要的内容(英雄区)移向次要区域。
css
.section {
opacity: 0;
transform: translateY(10px);
animation: slide-up 400ms ease-out forwards;
}
/* Stagger by index */
.section:nth-child(1) { animation-delay: 100ms; }
.section:nth-child(2) { animation-delay: 200ms; }
.section:nth-child(3) { animation-delay: 300ms; }
@keyframes slide-up {
to { opacity: 1; transform: translateY(0); }
}Hero Section "Bloom"
英雄区“绽放”效果
For hero sections, you might use a more complex animation:
- Background image fades in slowly.
- Heading slides in with a slight overshoot (spring).
- CTA button appears last with a crisp fade-in or subtle color transition.
对于英雄区,你可以使用更复杂的动画:
- 背景图片 缓慢淡入。
- 标题 带着轻微过冲(弹簧效果)滑入。
- CTA按钮 最后出现,采用清晰的淡入或微妙的颜色过渡。
Advanced: Optimistic UI
进阶:乐观UI模式
The fastest UI is one that doesn't wait for the server at all.
- The Pattern: Update the UI immediately assuming the server call will succeed. If it fails, roll back and show an error.
- Use for: Liking a post, toggling a switch, renaming a folder, deleting a message.
- Benefit: Instant gratification for the user, making the app feel "lightning fast."
最快的UI是完全不需要等待服务器响应的UI。
- 模式原理: 假设服务器请求会成功,立即更新UI。如果请求失败,则回滚并显示错误信息。
- 适用场景: 点赞帖子、切换开关、重命名文件夹、删除消息。
- 优势: 给用户即时反馈,让应用感觉“快如闪电”。
Adding Delight to the Wait
为等待过程增添愉悦感
Loading doesn't have to be a neutral experience. For waits longer than 2 seconds, consider adding brand personality and "delight" to keep the user engaged.
加载过程不一定是平淡无奇的。对于超过2秒的等待,可以考虑融入品牌个性和“愉悦感”,让用户保持参与度。
Brand-Aligned Micro-copy
符合品牌风格的微文案
Replace generic "Loading..." text with wording that reflects the brand's voice.
- Technical: "Compiling data...", "Syncing with cloud..."
- Playful: "Gathering pixels...", "Brewing your dashboard...", "Almost there!"
- Professional: "Preparing your report...", "Verifying details..."
用符合品牌调性的文案替换通用的“Loading...”文本。
- 技术风格: “正在编译数据...”, “正在与云端同步...”
- 趣味风格: “正在收集像素...”, “正在酿造你的仪表盘...”, “马上就好!”
- 专业风格: “正在准备你的报告...”, “正在验证详情...”
Branded Animations (Lottie/SVG)
品牌定制动画(Lottie/SVG)
For significant loading moments (initial app boot, complex data processing), replace the standard spinner with a small, brand-specific animation.
- A designer's tool might show a pencil drawing a line.
- A fitness app might show a pulsing heart or a moving runner icon.
- A financial tool might show coins stacking or a chart line moving upward.
在重要的加载场景(应用启动、复杂数据处理)中,用小型的品牌定制动画替换标准Spinner。
- 设计工具可以展示铅笔绘制线条的动画。
- 健身应用可以展示跳动的心脏或跑步者图标动画。
- 金融工具可以展示硬币堆叠或图表线条上升的动画。
Progressive Storytelling
渐进式叙事
If a wait is consistently long (3s+), use the loading area to tell a small story or provide value:
- Tips & Tricks: "Did you know you can use Ctrl+K to search?"
- Process Transparency: Show what the system is doing: "Checking database..." → "Optimising results..." → "Finalising view..."
如果等待时间持续较长(3秒以上),可以利用加载区域讲述小故事或提供实用信息:
- 技巧提示: “你知道可以用Ctrl+K进行搜索吗?”
- 流程透明化: 展示系统正在执行的步骤:“正在检查数据库...” → “正在优化结果...” → “正在完成视图渲染...”
Visual Transitions (Arrival)
视觉过渡(内容出现时)
When transitioning from a loading state to content, use a crisp fade-in (150ms) to make the arrival feel like a reward. Avoid scaling the incoming content, as it can cause layout instability.
从加载状态过渡到内容时,使用清晰的淡入效果(150毫秒),让内容出现的感觉像一种奖励。避免对进入的内容进行缩放,因为这可能导致布局不稳定。
Review Checklist
审核清单
- Is the loading state appropriate for the expected wait duration (spinner vs skeleton)?
- Does the skeleton screen match the physical layout of the incoming content?
- Is there a subtle shimmer animation on skeletons to signal "active loading"?
- Are buttons disabled during loading to prevent duplicate actions?
- Does content fade in over skeletons (150–200ms) rather than blinking into existence?
- For full-page loads, is a staggered entry used to guide the eye?
- Is respected for all loading animations?
prefers-reduced-motion - In "Optimistic UI" moments, is there a clear rollback path if the action fails?
- 加载状态是否与预期等待时长匹配(Spinner vs 骨架屏)?
- Skeleton骨架屏是否与即将加载的内容布局一致?
- 骨架屏是否有细微的闪烁动画以表明“正在加载”?
- 加载过程中按钮是否处于禁用状态以防止重复操作?
- 内容是否在骨架屏上方淡入(150–200毫秒)而非突然出现?
- 整页加载时是否使用交错入场动画引导用户视线?
- 所有加载动画是否尊重设置?
prefers-reduced-motion - 在“乐观UI”场景中,操作失败时是否有清晰的回滚路径?
Common Anti-Patterns
常见反模式
| Anti-pattern | Problem | Fix |
|---|---|---|
| A global spinner that blocks the whole app | High frustration, user cannot browse other areas | Use contextual loaders or skeletons |
| Skeletons that don't match the final layout | Massive layout shift (CLS) when data arrives | Match shapes and sizes exactly |
| Too many spinners on one page | Visual noise, feels like the whole app is broken | Group loading states into a single container skeleton |
| Faster-than-light skeletons | Shimmer animation that is too fast or high-contrast | Keep shimmer slow (1.5s+) and very subtle |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 阻塞整个应用的全局Spinner | 用户体验极差,无法浏览其他区域 | 使用上下文加载器或骨架屏 |
| 与最终布局不匹配的骨架屏 | 数据加载完成时出现严重布局偏移(CLS) | 完全匹配形状和尺寸 |
| 单页存在过多Spinner | 视觉噪音,让用户感觉整个应用出问题 | 将加载状态整合到单个容器骨架中 |
| 过快的骨架屏动画 | 闪烁动画过快或对比度太高 | 保持闪烁动画缓慢(1.5秒以上)且非常细微 |