news 2026/9/8 11:23:56

纯CSS3实现“加载中”文字动效:逐字弹跳、呼吸与波浪效果详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
纯CSS3实现“加载中”文字动效:逐字弹跳、呼吸与波浪效果详解

简介:纯CSS3 Loading文字加载动效是一份面向前端开发者和网页设计师的动画源码示例,动画核心完全由CSS3 animation驱动,无需依赖JavaScript实现加载反馈,适合网页加载初期使用。压缩包仅7KB,共7个文件,包括3个CSS样式文件、2个JS脚本文件、1个htm和1个html页面文件,CSS负责定义文字拆分与滚动动画,JS主要用于辅助文字拆分或后续交互扩展,说明文档则介绍了动画参数的调节方法。资源提供完整可运行的HTML示例页面,并展示了animation-name、animation-duration、animation-iteration-count、animation-direction等子属性的组合用法;通过调整animation-timing-function和animation-delay,还可快速改变滚动速度与节奏,便于扩展为左右滑动、旋转等更多效果。目前已有281人学习下载,适合想通过轻量案例理解CSS3动画原理、并为页面增加趣味加载效果的初中级前端人员。 上个月接了个活动页,视觉稿里“加载中”三个字带了一组很有识别度的动效,设计师特意强调要用品牌色渐变、逐字弹跳,而且文字本身还要有呼吸感。按老思路丢一张GIF进去,先不说服务质量,光是体积和色值就有得扯,换个活动主题就得重新导出。我直接改成纯CSS3 Loading文字加载动效——HTML就一个容器加几个span,动画全部交给@keyframes处理。做完之后整个资源包轻了不少,动效可以不依赖图片任意改色,后面几次活动我都是复制这套样式再调参数,省事很多。

先说结论:如果你也遇到“加载中”要动得好看、还要跟着设计走的需求,纯CSS3是性价比很高的方案。这篇文章我会从原理讲到实战,把几个常用的文字加载动效写法都拆开,附上可直接复制的代码和踩坑记录,适合前端入门到进阶的开发者参考。

1. 这个动效需求背后真正要解决的是什么

1.1 为什么偏偏是“文字”加载动效

页面加载时用户最怕的是“没反应”。转圈、进度条、骨架屏,本质上都是在告诉用户“系统还活着,你等一下”。但有些场景里,光有图形反馈不够——比如表单提交、支付确认、文件上传,这类操作有明确的状态语义,用户需要知道“正在执行的是哪一步”。这时候用文字做加载提示,信息传递比纯图形更直接,“正在提交”“正在校验”“加载中”这几个字本身就承担了解释功能。

文字加载动效的价值在于,它把“状态提示”和“视觉反馈”合在了一起。我见过不少项目用一张Loading图片,图片上写着“加载中”,结果图片是位图,换主题色还得重新作图。纯CSS3方案把文字作为真实文本存在页面上,样式随意控制,颜色、字体、大小、间距全部继承CSS,后期维护成本几乎为零。

1.2 相比GIF和JS动画库,CSS3赢在哪儿

先替GIF说句公道话:它确实简单,丢一个 进去就能转。但GIF在加载动效里有两个硬伤,一个是颜色受限,GIF格式只有256色,品牌色里常见的渐变蓝、渐变紫,导出来会有色阶断层;另一个是尺寸控制,为了清晰度做大了体积飙升,做小了模糊,而且GIF本身无法根据文字内容动态变化。

JS动画库(比如老牌的Animate.css、GSAP)能力确实强,缓动函数多、控制粒度高,但一个问题是不值得。一个只在加载状态出现几秒的动效,引入一个几十KB甚至上百KB的库,首屏性能白被拖累。CSS3动画直接跑在浏览器的合成器线程,部分变换和透明度变化不占用主线程CPU,动画丝滑程度并不输给JS方案。而且纯CSS3不需要等待JavaScript解析执行,页面结构出来就能动,稳定性也更好。

2. 核心动效原理:从关键帧到逐字控制

2.1 一个CSS3动画最基础的三个参数

写动画其实就是在告诉浏览器一件事:元素从什么状态开始,经过多长时间,变成什么状态,然后是否重复。这就落到三个核心属性上:

  • animation-name:绑定的是@keyframes关键帧的名称,关键帧里定义动画过程中每个阶段的样式。
  • animation-duration:一次完整动画的时长,单位是秒或毫秒,比如0.6s就是600毫秒。
  • animation-iteration-count:循环次数,加载动效一般用infinite无限循环。

我用一个生活化的类比解释:关键帧就像导演给演员写好的分镜脚本,0%时刻是什么表情,50%时刻变成什么表情,100%时刻又是什么表情;duration是一场戏的播放总时长;iteration-count是这场戏循环播放的次数。

2.2 animation-delay如何实现“逐字错位”

文字逐字弹跳的核心不是每个字自己有多花哨,而是“错位”这两个字。如果四个字同时弹跳,那看起来就是一个整体在动,没有逐字推进的感觉。让每个字比前一个字晚一点点启动,就会形成一种波浪式扫过的视觉效应。

这个错位由animation-delay控制。这里是新手容易出问题的地方:很多人以为设置delay就是让动画“延迟开始”,那动画开始前元素是什么状态?默认情况下,动画开始前元素应用的是自身原本的静态样式,动画结束后又回到静态样式。所以在逐字弹跳里,如果你只设置了delay,动画还没轮到这个字启动时,它可能保持完全静止,甚至和动画里的第一帧样式不一致,看起来会“闪”一下。

解决办法是配合animation-fill-mode: bothboth的意思是:动画开始前的等待阶段,强制应用第一帧的样式;动画结束后,如果有回放或停止,则保持最后一帧的样式。这样每个文字在还没轮到它动的时候,就已经是动画第一帧的“待启动”状态,整条时间线严丝合缝。

2.3 transform和opacity是动效的最佳组合

在动效设计里有个常见的“黄金法则”:做位移动效优先用transform,做显隐动效优先用opacity。原因在于这两个属性不会触发浏览器重新布局(layout),也不会触发重新绘制(paint),而是走合成器线程,由GPU直接处理,性能最好。

比如弹跳效果,我会用transform: scale()来做大小变化,而不是去改font-size。改font-size会引发附近文本的重新排版,代价昂贵;scale只是视觉上的放大缩小,不影响布局。同样,淡入淡出用opacity,不要去用display:nonevisibility切换,它们无法参与CSS动画的平滑过渡。这套思路不仅适用Loading文字,任何动效优化的底层逻辑都是这样。

3. 实操:完整实现“加载中”逐字弹跳动效

3.1 HTML结构:一个容器加四个span

先看结构,我这里用“加载中”三个字加三个点,意图是既有明确文字,又有轻微的节奏点缀。

<div class="loading" aria-label="加载中"> <span>加</span> <span>载</span> <span>中</span> <span class="dot">.</span> <span class="dot">.</span> <span class="dot">.</span> </div>

注意容器上加了一个aria-label="加载中",同时给内部文字标了aria-hidden(点也可以一并处理),这是给屏幕阅读器看的。因为CSS动效对读屏软件来说是不可感知的,如果页面里的“加载中”只有视觉呈现,盲人用户会完全不知道发生了什么。这个细节在项目验收时经常被忽略,但做无障碍审计时会被重点提到。

3.2 CSS:从静态样式到动画注入

先写静态样式,让文字排列好,再注入动画。

.loading { display: flex; justify-content: center; align-items: center; font-size: 32px; font-weight: 600; color: #4f6ef7; letter-spacing: 2px; } .loading span { display: inline-block; animation: loadingBounce 1.2s ease-in-out infinite both; } .loading .dot { animation-name: loadingDot; } .loading span:nth-child(1) { animation-delay: 0s; } .loading span:nth-child(2) { animation-delay: 0.15s; } .loading span:nth-child(3) { animation-delay: 0.3s; } .loading span:nth-child(4) { animation-delay: 0.45s; } .loading span:nth-child(5) { animation-delay: 0.6s; } .loading span:nth-child(6) { animation-delay: 0.75s; }

这里有个实用提醒:animation-delay的步长要根据总时长来推算。比如总时长1.2秒,想让6个字均匀错开,那每个字之间的间隔就是1.2s / 6 = 0.2s。我写0.15s是为了让波浪更密集一点,看起来节奏是错落有致,还是前后拥挤,完全靠这个数字微调。用flex布局后,span默认是行内元素,display:inline-block是必须的,否则transform在部分浏览器上不生效。

然后是核心的关键帧:

@keyframes loadingBounce { 0%, 60%, 100% { transform: scale(1); opacity: 0.4; } 30% { transform: scale(1.3); opacity: 1; } } @keyframes loadingDot { 0%, 60%, 100% { transform: translateY(0); opacity: 0.3; } 30% { transform: translateY(-8px); opacity: 1; } }

我把三个字设计成“放大+提亮”,三个点设计成“上浮+提亮”。文字和点用不同的关键帧,是为了避免视觉上全部一模一样,那样容易显得刻板。0%, 60%, 100%相同的关键帧位置,意味着一个周期里,元素有很长一段是静止的,只有中间一段发生动作,弹跳的“停顿感”就出来了。这也是加载动效常用的节奏控制:动一下、停一会儿、再动一下,比一直高频抖动更有呼吸感。

3.3 变体一:渐隐渐显的“呼吸”效果

弹跳动效偏活泼,适合年轻化的产品。如果做后台管理系统的加载态,更适合内敛一点的“呼吸”效果:文字整体柔和地变淡变亮,像呼吸一样,不打扰用户。

.loading { display: flex; justify-content: center; align-items: center; font-size: 28px; color: #5a6d7e; } .loading span { display: inline-block; animation: loadingFade 2s ease-in-out infinite both; } @keyframes loadingFade { 0%, 100% { opacity: 0.3; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-4px); } }

这个效果的要点是时长拉长,ease-in-out缓动配上2秒周期,文字在底部和顶部之间平滑过渡,视觉上非常安静。实际项目中我通常把这种动效用在“数据加载较慢但不想让用户焦虑”的场景,比如报表页等待接口返回。

3.4 变体二:底部水波纹效果

还有一种常见玩法是让文字“坐”在一排水波纹上,非常适合“进度刷新”类场景。水波纹本身是几个圆弧的缩放和位移。

.loading { display: flex; flex-direction: column; align-items: center; font-size: 24px; color: #333; } .wave { margin-top: 12px; display: flex; gap: 4px; } .wave i { width: 6px; height: 6px; background: #4f6ef7; border-radius: 50%; animation: waveMove 1s ease-in-out infinite both; } .wave i:nth-child(1) { animation-delay: 0s; } .wave i:nth-child(2) { animation-delay: 0.1s; } .wave i:nth-child(3) { animation-delay: 0.2s; } .wave i:nth-child(4) { animation-delay: 0.3s; } .wave i:nth-child(5) { animation-delay: 0.4s; } @keyframes waveMove { 0%, 100% { transform: translateY(0); opacity: 0.4; } 50% { transform: translateY(-10px); opacity: 1; } }

如果想把水波纹做得更细腻,可以再叠加一个scale变化,让圆点上升时缩小,下落时变大,模拟水波的挤压感。核心还是同一套“nth-child + animation-delay”的错位逻辑,你可以在这个基础上自由改颜色、数量、幅度。

4. 常见问题与排查技巧实录

4.1 动画不生效:百分之八十是这几种原因

我把平时遇到的“动画没反应”的排查顺序列成了一张表:

现象可能原因解决办法
动画完全不出现元素有display:none或父级隐藏检查元素是否实际在渲染树中
只有部分文字动没有写display:inline-block给每个动画目标加display:inline-block
动了一下就停了忘记写animation-iteration-count: infinite明确设为无限循环
文字位置抖动动画中transformtop/left混用统一用transform做位移
动画延迟阶段闪烁没设置animation-fill-mode: both加上both,让等待期应用第一帧样式
低版本浏览器不运行缺少-webkit-前缀补全-webkit-animation等前缀

实际开发里我踩得最多的是第二种。很多新手会直接把动画设置在span上,但span默认是行内元素,transform在部分浏览器上不生效,动画自然纹丝不动。加上display:inline-block或者display:block就能解决。

4.2 动画运行久了会“卡顿”“掉帧”是怎么回事

CSS3动画看起来很简单,但性能问题一点都不简单。最容易犯的错是动画里同时改了marginpaddingwidth这些布局属性,这些属性一旦变化,浏览器就得重新计算页面布局。如果是复杂页面,布局一旦频繁重算,掉帧肉眼可见。

性能优化的原则很简单:

  • 动画只使用transformopacity
  • 如果需要渐变色变化,优先考虑用伪元素叠加一个透明度的变化,而不是直接给背景色加过渡。
  • 给常驻的动效元素加一句will-change: transform;,提前告知浏览器哪些属性会变,让它做优化准备。但要注意别滥用,will-change使用过多反而会占用大量内存。
  • 如果页面同时有多个Loading动效,尽量错开它们的动画周期,避免同时到达最复杂的帧。

我碰到过一个比较极端的案例,页面上一共挂了8个带动画的模块,每个动画里都用了filter: blur()做模糊效果,结果低端手机上滚动页面像幻灯片一样。后来把模糊效果改成用半透明背景模拟,性能立刻恢复。加载动效本身是“锦上添花”,千万别让它成为性能瓶颈。

4.3 用户偏好问题:动效可以关掉吗

最后提一个很容易被忽略的点:系统层面有“减弱动态效果”的偏好设置,很多没有意识到这个选项的用户也可能开启它。CSS里有一个媒体查询专门处理这种情况。

@media (prefers-reduced-motion: reduce) { .loading span { animation: none; } .loading span { opacity: 1; } }

这样处理之后,对于明确表示“我不喜欢动画”的用户,页面上只显示静态文字,不会因为强制动画造成晕眩等不适。这个细节在B端产品里尤其重要,因为后台管理员可能一天要用十几个小时,闪烁的动画长时间挂着确实影响体验。

4.4 更好的做法:把Loading做成可复用组件

写到这里,我偏向于建议你把做好的动效封装成一个组件,而不是每个页面都复制一份HTML和CSS。封装时需要注意两点:文字内容要能配置,这样“提交中”“保存中”“加载中”都能共用一套动画;文字颜色和字号也要暴露成变量,方便不同的使用场景做样式覆盖。

.loading { --loading-font-size: 32px; --loading-color: #4f6ef7; --loading-delay-step: 0.15s; font-size: var(--loading-font-size); } .loading span { animation-delay: calc(var(--loading-delay-step) * var(--i, 0)); }

这里我用了一个--i自定义属性配合nth-child单独设置,比如.loading span:nth-child(1) { --i: 0; },然后延迟时间就可以通过calc()计算出来。改动一个CSS变量就能调整整个动画的节奏,比逐字改animation-delay灵活很多。这个思路在你接手别人的项目、需要快速配出不同Loading风格时,特别实用。

最后再分享一点我的体会

做动效不只是“让页面更好看”,它其实是在替产品说一句话:“别走,马上就好。”纯CSS3方案之所以值得掌握,是因为它在性能、可维护性、可访问性之间拿到了一个平衡点。我后来做活动页,设计稿里的各种Loading场景,基本都能用这套思路快速搭出来,改个延迟时间、换一组关键帧,又是一个全新的质感。

如果你接下来要自己动手做,我建议你从今天的第一个弹跳案例开始,先把“nth-child + animation-delay + fill-mode: both”这套组合吃透,再去试着给每个文字增加不同的transform,做出属于自己的文字Loading效果。动效这个东西,看十遍不如亲手调一遍参数。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 11:22:59

mbed OS源码深度剖析:从HAL到RTOS的嵌入式架构设计

1. 从一次真实项目说起&#xff1a;为什么我要啃 mbed OS 源码如果你做过物联网设备&#xff0c;尤其是那种需要在多种 Arm 芯片上快速切换方案的量产项目&#xff0c;你一定遇到过这组连环问题&#xff1a;前期在 STM32 上调好的逻辑&#xff0c;换到 NXP 或者瑞萨的片子&…

作者头像 李华
网站建设 2026/9/8 11:22:35

RKNN NPU推理实战:78.78ms耗时背后的版本对齐与优化指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 11:21:46

Java面试避坑指南:从搞笑程序员翻车现场拆解面试官真实考点

面试这事儿吧&#xff0c;我经历过太多轮了。坐在桌子这边当候选人时慌&#xff0c;坐在桌子那边当面试官时也慌——只不过慌的内容不一样。前段时间朋友发我一个视频&#xff0c;标题就叫“互联网大厂Java面试&#xff1a;严肃面试官与搞笑程序员的对决”&#xff0c;我点进去…

作者头像 李华
网站建设 2026/9/8 11:20:21

C# WebApi与IIS解耦:Kestrel自宿主+反向代理实战

简介&#xff1a;面向C#开发者的WebApi服务示例&#xff0c;重点演示如何通过自托管模式构建不依赖IIS的服务&#xff0c;解决跨平台部署与微服务扩展中的环境依赖问题。压缩包共188个文件、约5.92MB&#xff0c;除cs源文件与csproj工程文件外&#xff0c;还包括较多dll动态库、…

作者头像 李华
网站建设 2026/9/8 11:19:39

CDMA通信系统Simulink建模与仿真:扩频、RAKE接收与误码率分析

简介&#xff1a;针对CDMA通信系统原理与Simulink建模需求&#xff0c;这份资源面向通信工程专业学生、MATLAB初学者及需要完成相关课程设计或仿真的工程师。内容围绕CDMA多址接入与扩频通信展开&#xff0c;采用M序列作为扩频码&#xff0c;分别对理想正交码组和非完全正交码组…

作者头像 李华
网站建设 2026/9/8 11:18:28

设备树与Linux驱动开发:从原理到RK3568实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华