简介一款纯 CSS3 实现的 Loading 文字加载动效源码面向具备前端基础或网页交互设计经验的开发者尤其适合在网站初次加载、接口请求等待以及图片懒加载等场景使用。核心效果完全依靠层叠样式表动画属性驱动无需额外引入大型动画库即可让文字呈现上下滚动、节奏变化的动态反馈。资源压缩包共 7 个文件包含 3 个样式文件、2 个脚本文件和 2 个演示页面整体仅 7 千字节。样式文件兼顾文字拆分结构与关键帧动画时间曲线脚本负责字符切分和加载完成后的状态切换结构清晰便于二次开发与直接移植。已有 281 人浏览学习说明这套写法在纯 CSS 动画场景中有一定参考价值。通过演示页还可拆解动画名称、持续时间、缓动函数、延迟、方向、播放次数、填充模式与播放状态等参数的实际用法快速验证后即可应用到自己的加载提示或文字动效项目中。1. 纯CSS3 Loading文字加载动效不用一行JS的文字弹出动画怎么做做前端页面时我最怕的不是功能写不出来而是加载状态太单调。转圈转久了显得廉价干等又没信息反馈。后来下载了一个“纯CSS3 Loading文字加载动效”源码包发现它把一行“LOADING…”拆成独立文字逐个弹出、回弹、再循环整个过程一行JS都没写完完全全用CSS3的animation、transform和opacity撑起来。这类Demo在源码站常常被归到“JS特效”或“JS常用代码”分类下但点进去你会发现它其实是纯CSS实现适合前端页面初始化、表单提交、骨架屏过渡等场景。对于想快速给页面加一个有质感的文字特效、又不想引入动画库的人来说这个源码包基本是零成本的选择。2. 项目设计与技术选型动画链路上的三个关键角色2.1 项目定位这其实是一份“打开就能跑”的前端Demo从资源形态来看这份“纯CSS3 Loading文字加载动效”不是完整的工程化项目没有package.json、没有webpack配置、没有node_modules它就是一个静态Demo包。最常见的文件构成是index.html和style.css两个主体文件再加一份说明文档。下载之后放到浏览器里双击index.html就能看到效果不需要本地起服务、不需要装依赖和那些需要npm install的工程源码相比它的使用门槛几乎为零。适合用到这个Demo的人画像是写H5活动页的前端、做管理后台系统模板的开发者、以及刚接触CSS3动画的初学者。比如一个运营页面在拉取数据时需要一个“系统正在拼命加载”的过渡态把这份源码里的文字动画稍作调整替换成业务文案就能当作加载提示用。它不需要和高复用组件库耦合反而因为代码足够少方便直接移植到自己的项目里。如果你是从源码站下载的这份资源打开说明文件多半会看到这样的提示直接修改index.html里的文字再把style.css里对应的动画类名对上即可。这点和使用大型框架项目不同它没有构建步骤也没有依赖树因此即使你只懂一点HTML和CSS也能在几分钟内把效果跑起来。对于想学习CSS3动画原理的初学者来说它也是一个很好的入手样本代码量小到可以把每条规则都翻一遍。2.2 技术选型分析为什么纯CSS3能在Loading场景下替代JS我见过不少类似的文字加载特效实现方式无非三种用JavaScript定时器逐字添加class用CSS3 animation配合animation-delay或者直接引一个动画库。这个Demo选的是第二种也是我认为在“逐字弹出”这个需求上性价比最高的方案。它的原理说起来很简单CSS3的keyframes定义动画关键帧animation属性把动画绑定到元素上再用animation-delay让每个文字延迟不同的时间开始视觉上就形成了“逐个蹦出来”的先后关系。核心用到的能力包括animation、keyframes、transform、opacity这些都是现代浏览器已经稳定支持的基础特性不需要任何Polyfill。为什么不用JS一方面CSS3动画由浏览器合成器线程处理通常比JS操作DOM的setInterval更省CPU另一方面这类动效不需要交互逻辑用声明式的CSS描述状态变化后续维护时只看样式文件就能改速度、改延迟、改缓动比在JS里找定时器代码直观得多。当然如果你要实现“点击按钮重新播放”这种带交互的行为那就必须借助JS了但这份Demo的定位是纯展示型加载态不需要交互所以纯CSS是合理的。还有一个容易忽略的点JS定时器实现的逐字动画如果页面切到后台再切回来定时器默认会被浏览器的节流策略拖慢出现动画卡顿。而CSS3动画在大多数情况下能保持相对稳定的时间节奏这就是为什么很多生产环境的加载动效都会优先选择纯CSS实现而不是写一个setInterval。当然CSS3动画也不是完全没有黑匣子比如复合图层占用和GPU内存但在这个Demo的量级下那些问题几乎可以忽略。2.3 目录结构与文件说明源码包里到底有什么这份源码包的目录结构大致是这样的css3-loading-text/ ├── index.html ├── style.css └── 使用说明.txt文件作用是否需要修改index.html文字节点结构每个字母用一个span包裹改文字时必改style.css动画定义、延迟时间、颜色、字体等全部样式调动画速度/颜色时改使用说明.txt作者留下的参数说明和兼容性提示可读可不读有的版本可能已经在一份HTML里写好了内联样式目录更简单。无论哪种核心逻辑都一样能跑起来的文件有多少不重要重要的是“文字拆分”和“动画延迟”这两个操作在哪个文件里完成。如果是第一次看这种Demo建议先用没有压缩的版本带注释的代码更容易对照阅读。这个结构也意味着如果你要把动效集成到已有项目里只需要把style.css中相关的动画规则复制过去再把HTML结构中的span序列拷贝进目标页面就能立刻渲染出效果。不需要额外的加载器也不需要保证文件路径这正是它作为源码下载资源最大的优势。3. 核心代码拆解一个文字从透明到落地的完整周期3.1 HTML骨架把Loading文案拆成独立span先看index.html最核心的部分。通常是一个容器元素里面每个字母一个span。示例div classloading spanL/spanspanO/spanspanA/spanspanD/span spanI/spanspanN/spanspanG/spanspan…/span /div这里没有用标签包裹整段文字而是把“LOADING…”按字符拆成8个span。拆开的意义在于每个span可以独立绑定animation-delay否则整段文字只能整体弹出做不出“逐字”效果。注意标点符号“…”也被当成了一个span这样延迟时间同样适用于省略号动画节奏才完整。实际项目中如果需要中文文案直接按字拆就行例如“加载中”三个字就拆成三个span。但有一个细节中文标点或英文连续字符不要误拆比如“Loading”如果拆成“L o a d i n g”没问题但带有空格的词组要小心空格也占一个span。空格本身不可见但如果延迟动画作用在空格上它会在布局中占位导致前后字母的弹出节奏出现一个“空洞”观感上像是字与字之间突然顿了一下。另一个在HTML阶段就要注意的点是文件里可能给容器加了role或aria-label等无障碍属性。常见做法是把整段文案放进去方便读屏器朗读避免读屏器把拆散的字母逐个读出来。比如div classloading aria-labelLOADING… spanL/spanspanO/spanspanA/spanspanD/span spanI/spanspanN/spanspanG/spanspan…/span /div这是因为屏幕上显示的是拆散字母但语义上仍应是一个完整的词。如果没写也没关系但二次开发时加上这是一个很小的成本顺便能把可访问性补上。这样在无障碍模式下用户听到的是“LOADING…”而不是“L、O、A、D、I、N、G、省略号”。对于做H5活动页的同学来说可能觉得无所谓但如果你的页面要过无障碍检测这一步就是实打实的加分项。还要说明一点这种结构不是唯一的也可以用::before/::after伪元素配合content但那样只能同时控制两个字符灵活性太差。所以主流写法还是显式的span列表。有人会问为什么不直接用CSS的word-spacing或letter-spacing来做文字间距原因是它无法解决“每个字单独动”这个核心需求。拆成span是最直观也最好维护的方式代价是HTML里多出几个标签但浏览器解析这点节点的开销完全可忽略。3.2 CSS3动画keyframes与animation-delay的配合打开style.css核心部分可以分为三块第一块定义keyframes第二块绑定到span上第三块用:nth-child或子元素选择器设置延迟。常见的keyframes实现大概是这样的keyframes loading-bounce { 0% { opacity: 0; transform: translateY(30px) scale(0.8); } 50% { opacity: 1; transform: translateY(-10px) scale(1); } 100% { opacity: 1; transform: translateY(0) scale(1); } } .loading span { display: inline-block; animation: loading-bounce 1.2s ease-in-out infinite; }逻辑说明动画分三段。0%时文字透明且向下偏移30px、缩小到0.8倍看起来是“刚从底部冒出来”50%时透明度变为1并向上回弹10px产生越过目标点的效果100%时回到原始位置、原始大小。整个动画周期1.2秒采用ease-in-out缓动无限循环。display: inline-block是必须的因为span默认是inline元素transform对inline元素无效加了inline-block才能让translateY、scale生效。这里的translateY(30px)表示在Y轴方向向下位移30像素scale(0.8)表示缩放到原始尺寸的80%。位移和缩放组合起来能让文字看起来像从底部被“弹”起来而不是简单地从透明度上变化。如果你希望动效更柔和可以把30px改成10px、把-10px改成-4px减少位移距离会让整个动作显得更轻巧。反过来如果希望强调“加载中”的跳脱感可以把-10px改成-24px让回弹幅度更大视觉冲击力更强。接下来是逐字延迟。常见做法是这么写.loading span:nth-child(1) { animation-delay: 0s; } .loading span:nth-child(2) { animation-delay: 0.1s; } .loading span:nth-child(3) { animation-delay: 0.2s; } .loading span:nth-child(4) { animation-delay: 0.3s; } .loading span:nth-child(5) { animation-delay: 0.4s; } .loading span:nth-child(6) { animation-delay: 0.5s; } .loading span:nth-child(7) { animation-delay: 0.6s; } .loading span:nth-child(8) { animation-delay: 0.7s; }延迟时间从0开始每多一个字符增加0.1秒最多加到0.7秒。这样第一个字先弹第二个字比第一个晚0.1秒视觉上形成“波浪式”推进。关键点是动画是无限循环的每个元素都从自己的delay开始循环前几轮会有错位之后就一直维持这个相位差。无论页面加载过程中动画从哪个时刻开始最终每个字符都会保持0.1秒的间距看起来像一组连续的弹跳波。参数说明这样写扩展性不好但Demo通常就这样写。如果字少比如只有“加载中”延迟序列改为0、0.12、0.24如果想让节奏更紧凑就把步长从0.1s改成0.06s。计算延迟时有一个笨办法第一个延迟设为0后面的延迟等于前一个加上单次动画周期的1/NN是总字数这样一整波动画结束刚好轮到第一个字再启动达到无缝循环。不过大多数时候0.1s步长已经够看了不用纠结精确无缝。这里要特别说一下负的animation-delay。如果你想让页面一打开时动画就已经处于“中间位置”直接设置负延迟就行比如animation-delay: -0.3s会让元素直接从动画开始后的0.3秒处播放配合infinite循环可以模拟出已经运行了一段时间的效果。但在这个Demo里通常不需要负延迟因为Loading本身才出现用户从零开始看整个动画是符合预期的。3.3 参数调节速度、延迟、间距怎么调才自然这一节讲具体调参数。先贴一个修改后的例子.loading { font-size: 32px; letter-spacing: 4px; }常见问题是逐字拆开后浏览器会把span当作inline元素空格和间距会失效。如果要把字间距拉开letter-spacing要在父容器.loading上设置而不是在span上设置因为span之间会自动紧凑排列。在span上设margin也可以但margin会直接影响动画位移的计算最好统一用letter-spacing。速度调整animation: loading-bounce 1.2s ease-in-out infinite 中的1.2s就是单字完成一轮动画的总时长。想要猛烈一点就缩短到0.8s想要柔和一点就拉长到1.6s注意这个值改了之后animation-delay的步长最好也等比缩放否则会出现后半段文字还在陆续进场前半段已经循环完的情况。这也是新手最容易翻车的地方光改duration不改delay动画节奏就乱了。我一般会先定下duration然后把延迟步长设为duration的1/10比如duration是1.2s步长就是0.12s八个字最大延迟约0.84s整体看起来波浪比较完整。另外颜色的渐变也可以纯CSS完成。比如给每个span设置不同的color或者用background: linear-gradient配合background-clip: text做渐变文字。但要注意background-clip: text在部分浏览器下需要加-webkit-前缀而且动画中如果不小心引入了宽高变化文字背景会被截断。建议在动画稳定后再加这些视觉效果避免调动画时还要同时排查样式污染。这里整理一份常用的参数调节表直接照着抄就行参数位置当前值调节方向效果animation后第一个时间1.2s改小到0.8s节奏更快更有紧迫感animation后第一个时间1.2s改大到1.6s节奏更慢更优雅animation-delay步长0.1s改小到0.05s波浪更密集像整体浮动keyframes中translateY30px / -10px两值都缩小动作幅度变小更安静font-size32px按需改字体变大时最好同步加大translateY位移letter-spacing4px按需改控制字间距避免字母粘连调参时建议用浏览器开发者工具直接改CSS变量或animation相关属性实时预览。如果你把样式放在style.css里每次保存后刷新页面就好不需要清缓存因为这是纯静态页面。4. 避坑排查动效不动的三个高频原因4.1 现象一动画只在刷新瞬间执行一次有同学把代码复制到自己的项目后发现第一次打开页面字母一个个弹出来然后就全部静止在原地再也不动了。原因很简单他去掉或漏写了animation-iteration-count: infinite或者写成了animation: loading-bounce 1.2s ease-in-out;默认的iteration-count是1动画播放一次就结束。解决方法是显式加上infinite关键字或者单独声明animation-iteration-count: infinite。检查方法也简单打开浏览器开发者工具选中某个span在Styles面板里看animation简写属性是否包含infinite。如果只是少了循环次数补上就行。但有一种微妙情况是动画循环了但看起来像静止比如动画首尾关键帧一样且循环过程中没有明显的位移或透明度变化这就要看关键帧定义是否真的把状态拉开了。很多人以为循环次数是原因实际上关键帧没写对。遇到这种情况先手动把opacity从1改成0再刷新如果文字消失了说明动画还在跑只是关键帧差异太小。这条踩坑记录在群里见过好几次最常见的翻车点其实是简写属性覆盖。比如你先写了animation-name: loading-bounce又在后面写了animation: 1.2s ease-in-out infinite后者会把前一个名称覆盖掉导致动画变成空。简写属性很容易把显式声明的值吞掉排查时优先看Computed面板里实际生效的animation-name。4.2 现象二复制的代码里字母全部叠在一起页面能看到文字但所有字母挤在同一位置看不出逐个弹出的先后。这个问题的根源大多是父容器和span的display属性被覆盖了。在Demo源文件里span被设成display: inline-block如果你把它复用到自己的工程里项目全局样式可能对span有reset例如display: inline或者容器设置了flex布局导致span变成flex item原本的transform位移逻辑虽然还在但布局方式变了字母不再按顺序水平排列而是被flex的伸缩逻辑拉成叠放。解决思路先分清来源如果容器是flex布局那么span上的translateY仍然生效但水平方向的位置由justify-content和flex决定不再是inline-block的自动排序。要让它们水平排列可以在span上继续使用display: inline-block并移除容器上的flex或者接受flex布局用gap控制间距。另一个叠放的原因是animation-delay设置了负数或很大的值导致多个span处于同一相位看起来像同一个位置。检查delay列表是不是从0开始等差递增并且没有出现负值负值在动画中会让元素从动画中间阶段开始容易产生状态错乱。如果你看到字母不是叠在一起而是出现“先散开再聚拢”的乱序波动那多半是keyframes里用了translateX和translateY混搭每个span的位移方向被nth-child规则改成了不同的角度。这类代码有些Demo会故意做成从四周飞入的效果但如果你只是想要“逐个弹出”所有span的位移方向应该保持一致改动前先看清楚原作者的意图不要抱着“每个字都飘进来更酷”的想法直接魔改结果改出乱跳的字母阵。4.3 现象三Safari / 旧内核浏览器不显示动效在Chrome上跑得好好的拿到iOS Safari里动画就没反应这是CSS3动画兼容性里比较常见的坑。旧版Safari对部分transform属性需要-webkit-前缀尤其是-translateY、-scale这类组合写法直接写transform: translateY(30px) scale(0.8); 在某些内核版本里会被忽略。另一个原因是keyframes命名冲突Safari对部分保留关键词和非法字符处理更严格。一般处理方法是给keyframes名称用普通英文比如loader-bounce不要用中文或带连字符开头的名称transform必须写完整例如-webkit-transform: translateY(30px); 如果项目还在兼容iOS 9以下就需要同时写带前缀和不带前缀的版本。当前这份Demo的定位是纯CSS3并没有做过多前缀兼容这是源码资源常见的情况引入到生产环境前建议用autoprefixer过一遍不要手改几十条前缀。兼容性另一个隐蔽坑是prefers-reduced-motion。有些用户的系统开启了“减少动态效果”Safari和Chrome会把CSS动画自动降级或直接停掉。这不是Bug而是浏览器在按用户偏好行事。如果产品要求必须让所有用户看到动画可以考虑在禁用动画时展示一张静态图但更推荐的做法是尊重用户设置把动画时间缩短或者只在无偏好时启用。做页面动效久了你会发现有时候不是代码写错而是浏览器在替你执行用户的选择。4.4 现象四动画一多CPU占用升高文字出现抖动这类动画在低端安卓机上做无限循环时如果同时有多个实例在页面上跑可能会出掉帧。问题出在transform和opacity以外的属性也被放进了动画。比如在keyframes里改了letter-spacing或font-size这两个属性会触发重排而不仅仅是合成器层面的合成。只要触发了layout性能就会断崖下跌。解决方法是把动画属性严格限制在transform、opacity这两个属性内。如果要改变间距静态设置在span上如果要让文字变色那是color虽然不触发layout但会引起重绘也尽量少用。在这个Demo里只要确认keyframes里只有opacity和transform一般就不会有大的性能问题。如果机器实在跑不动最简单的做法是把无限循环改成有限循环例如animation-iteration-count: 3等动画播完三遍就停下来给用户一个静态结果。还有一个小习惯动画元素不要画得太大。如果你把font-size调到80px以上再叠加GPU合成移动端可能会把整块图层推到合成器造成滚动页面时文字区域发虚或抖动。我一般会控制在40px以内如果产品需要大面积加载提示就用背景色块代替大号文字动画。这种问题在PC上很难复现只有上了真机才看得到说是玄学其实就是合成层面积和内存带宽的问题。5. 二次开发把固定文案改造成通用 Loading 组件5.1 改造思路写一个模板生成函数替换手工span手工在HTML里拆字能撑住8个字符的Demo但换成“系统数据加载中”这种长文案手动维护会累。常见做法是保留CSS3动画不动只把HTML生成部分交给模板函数处理。比如用Vue的话直接在模板里循环const loadingText LOADING…然后用v-for生成span列表再把延迟通过index计算成内联style的animationDelay。注意要保留纯CSS的优雅动画定义仍然在CSS里生成的只是每个span的延迟差异。JS只负责“生成节点”不负责“驱动动画”。这样原本的CSS3纯动画逻辑没有破坏只是从“写死”变成了“动态生成”算是二次开发里最安全的改法。5.2 验证方法用DevTools模拟慢速播放检查节奏改造完之后最快的验证方式是打开开发者工具的Rendering面板开启FPS Meter看掉帧也可以降低动画速度比如临时把duration改成3s慢放观察每个span的弹出时刻是否符合预期。确认无误后再把速度改回来。每次改参数我都建议单独开一个空页面粘贴样式验证不要直接在大项目里反复刷新避免被其他样式干扰判断。如果要检查无障碍属性可以用控制台临时删掉aria-label看读屏器是否把字母逐个读出来。从那以后我每次上线带Loading动效的页面都会强制在低端机配置下看一眼至少确认动画不会长期占用CPU。如果你只想要一个快速可用的文字加载特效这个纯CSS3 Demo的代码量足够小小到你能在十分钟内读懂每一行这是很多重量级库做不到的。希望帮到你。本文还有配套的精品资源点击获取