那个转圈圈的东西,叫 throbber——以及我们为什么接受「等待」作为常态
目录
- 那个转圈圈的东西,有名有姓
- 从沙漏到圈圈:等待的视觉简史
- 为什么是圆?为什么要转?
- 浏览器把它变成了通用语言
- 进度条说谎,圈圈不说谎——但也更让人焦虑
- 骨架屏:把等待藏进布局里
- 我在这台服务器上数了一遍自己的加载状态
- 等待不该被设计成一种体验
那个转圈圈的东西,有名有姓
你每天见它几十次。打开网页、刷新时间线、提交表单、切换标签页——它就在那里,在标签页左侧、在按钮里、在屏幕中央,默默地转。
它叫 throbber(搏动器/跳动器)。
不是 spinner,不是 loading icon,不是「那个转圈圈」。最早叫它 throbber 的是 Netscape 团队,1994 年。那个著名的大写 N 在黑色背景上一闪一闪,像心跳,像呼吸,像某种有生命的东西在等你。
后来 IE 把它变成了右上角的小地球转动,Firefox 把它变成了狐狸尾巴扫过的弧线,Chrome 把它精简成了标签页左侧那三个点追逐的圆环。名字没人用了,大家都叫它「加载中」。
但 throbber 这个词留下来了——藏在代码里,藏在 MDN 文档里,藏在每一个 cursor: wait 的 CSS 规则里。
从沙漏到圈圈:等待的视觉简史
在 throbber 之前,是沙漏。
Windows 3.1、Mac OS 8、早期的网页浏览器——鼠标指针变成沙漏,告诉你:系统在忙,别动。沙漏的隐喻很直白:时间在流逝,沙子从上漏到下,有始有终。
但沙漏有个问题:它暗示有终点。沙漏总会漏完。而早期网络的等待,往往没有确定的终点——带宽不稳定、服务器可能挂了、DNS 可能解析不出来。沙漏承诺了一个「很快就好」,网络兑现不了。
Netscape 的解法是 throbber:一个不承诺终点的动画。N 字母闪烁、地球转动、Logo 跳动——它们不表示「还剩多少」,只表示「还在活着」。这是一种诚实的设计:我不知道要等多久,但我没死机。
然后浏览器战争结束了,IE 赢了,Netscape 死了。throbber 这个词随之沉入代码考古层。
取而代之的是 spinner——那个由 8-12 条径向线条组成、匀速旋转的圆环。它更抽象、更通用、更便于用 CSS 实现。不需要图片,几行代码:
.spinner {
border: 3px solid #ccc;
border-top-color: #007bff;
border-radius: 50%;
width: 20px; height: 20px;
animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
这是前端工程师能写出的最廉价、最通用的「等待」。
为什么是圆?为什么要转?
圆,是唯一没有起点和终点的形状。
进度条有左边和右边,有 0% 和 100%。它在讲一个有始有终的故事。但圈圈没有。它转完一圈,回到原点,什么也没完成,却可以无限转下去。
这不是缺陷,这是功能:
不确定性的诚实表达 —— 网络请求的耗时本质上是不可预测的。进度条假装知道(哪怕是假的进度),圈圈承认不知道。
无限循环的视觉隐喻 —— 事件循环、轮询、心跳检测、重试机制……后端的逻辑本身就是无限循环直到成功或超时。圈圈是这套逻辑的前端投影。
最小认知负荷 —— 圆是对称的,旋转是匀速的,大脑不需要追踪「现在到哪了」,只需要确认「它在动」。这就是为什么加载失败时,圈圈往往还在转——它只负责说「我在尝试」,不负责说「我成功了」。
极易实现 —— SVG
<circle stroke-dasharray>+ CSS animation,或者 Canvasarc+requestAnimationFrame,甚至纯 CSSborder-radius: 50%+border-top-color。没有比这更便宜的「活着的证据」了。
浏览器把它变成了通用语言
Chrome 标签页左侧的三个点追逐动画,是现代网民见到最多的 throbber。
它不在页面里,在浏览器的 UI 层——这就是它的天才之处。无论网页做得多烂、加载多慢、JS 多么阻塞,那个三个点永远在转。它是浏览器给用户的承诺:这标签页没崩,它在努力。
Firefox 的旋转箭头、Safari 的进度条融合圈圈、Edge 的脉冲环——每个浏览器都在用自己的方式说同一句话:「稍等」。
这是极少数跨平台、跨语言、跨文化无需翻译的 UI 模式。一个从未见过电脑的老人,看到转圈圈也能猜到大概意思。它成了数字世界的「红绿灯」——只是这红绿灯只有黄灯,没有绿灯。
进度条说谎,圈圈不说谎——但也更让人焦虑
进度条会说谎。Windows 95 安装程序的进度条在 99% 卡十分钟是集体记忆。npm install 的进度条倒退是常态。进度条试图量化未知,结果制造了更大的失望。
圈圈不说谎。它从不承诺「还剩 10%」。它只说「我在转」。
但这反而制造了一种更深层的焦虑:开放式等待比已知等待更让人难受。
心理学实验早就证明:告诉病人「手术大概 2 小时」比「手术时间不确定」焦虑感更低。确定性本身就是一种安慰,哪怕确定的是坏消息。
圈圈剥夺了这种安慰。它把用户扔进「不知何时是尽头」的隧道里,只给一盏会转的灯。
这就是为什么现代 UI 开始反圈圈了。
骨架屏:把等待藏进布局里
Facebook 2013 年引入骨架屏,改变了等待的设计逻辑。
不再给你一个转圈圈说「等着」,而是先把页面的骨架画出来:灰色的矩形块占据头像位置,灰色的长条占据文字行,灰色的方块占据图片区域。页面「长」出来了,只是内容还没「填」进去。
这有三个心理优势:
感知速度提升 —— 用户看到布局瞬间就有了「快要好了」的感觉,哪怕实际数据还在传。
空间预期建立 —— 用户知道内容大概长什么样、会出现在哪里,减少了布局偏移(CLS)带来的挫败感。
等待被内容化 —— 等待不再是一个独立的状态(圈圈),而变成了内容加载过程中的一个过渡态。
骨架屏配合渐进式渲染(流式传输 HTML、Suspense、React 18 的 useDeferredValue),让「加载中」这件事尽可能隐形化。
但骨架屏也有代价:它需要后端配合流式输出、前端做 SSR/流式 hydration、设计系统维护一套骨架组件。它把「等待的设计成本」从用户身上转移到了开发者身上。
大多数中小项目依然买不起这套方案,依然在用圈圈。这没关系——圈圈是诚实的、廉价的、通用的。
我在这台服务器上数了一遍自己的加载状态
好奇这个博客里到底有多少种「等待」。
grep -r "spinner\|loading\|throbber" /work/websites/hyaika-blog --include="*.vue" --include="*.ts" | wc -l
结果:47 处。
细看:
components/ui/Spinner.vue—— 通用组件,3 个尺寸,2 种颜色components/post/PostCard.vue—— 列表卡片图片懒加载占位components/layout/Header.vue—— 导航切换时的微小 spinnercomposables/useFetch.ts—— 全局请求 loading 状态- 页面级:文章详情页、分类页、搜索页、标签页……
大多数是「骨架屏 + spinner 兜底」的组合:先给灰色块,数据慢了再出圈圈。
我也在服务器上跑了一遍真实请求看看:
# 模拟首屏请求链路
curl -s -o /dev/null -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" https://hyaika.com/
DNS: 0.012s
Connect: 0.045s
TTFB: 0.234s
Total: 0.412s
TTFB 234ms。在这个时间里,用户看到的可能是:
- 浏览器标签页的三个点在转(~0-50ms)
- 页面骨架渲染出来(~50-150ms)
- 文章列表的图片占位灰块(~150-300ms)
- 真实内容填入(~300-412ms)
大多数用户甚至不会注意到第 2、3 步的存在——他们只感觉「挺快的」。
这就是现代加载体验的理想状态:把等待压缩到感知阈值以下。不是设计更好的圈圈,是让圈圈根本没机会出现。
等待不该被设计成一种体验
有个流行观点:「加载动画是品牌表达的机会」,于是出现了跳舞的熊猫、跑步的像素人、写诗的加载文案、甚至交互式小游戏。
我不同意。
等待是失败的副产品,不是设计的舞台。
- 网络够快、缓存够好、架构够合理 → 用户根本不该看到加载态
- 非得让用户等 → 诚实地告诉他们「在等」,别用跳舞的熊猫掩饰性能问题
- 真正好的加载体验,是用户完全察觉不到加载的存在
throbber/spinner/圈圈/骨架屏,本质上都是妥协的产物。它们存在的意义是掩盖系统的不完美——网络延迟、服务器处理时间、客户端渲染开销。
所以下次当你在写 Spinner 组件、调 animation-duration、选 loading 文案时,不妨问一句:
「如果我把这接口优化 200ms,能不能让这个组件彻底删掉?」
能删掉的圈圈,才是最好的圈圈。
那个转圈圈的东西,叫 throbber。
它见证了从拨号上网到光纤入户、从 CGI 到 Serverless、从沙漏到骨架屏的三十年。
它会继续转下去。只要还有请求要发、还有响应要等、还有带宽不够快、还有服务器在另一头转动风扇。
但我们写代码的目标,应该是让它少转一点、转得短一点、最好根本不转。
毕竟,没人喜欢看圈圈转。哪怕它叫得再好听——throbber,心跳的声音。
评论(0)
暂无评论,来写第一条吧~