ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

150款CSS3 loading动画拆解:从实现原理到组件化落地

150款CSS3 loading动画拆解:从实现原理到组件化落地 做了六年前端收藏夹里躺着几百个loading动画的链接平时团队做项目我翻得最多的也是这类素材。前阵子整理资料我把手头攒下的150款CSS3 loading加载动画重新按风格、动画原理、适用场景过了一遍发现很多收藏其实处于“躺灰”状态真到要用的时候还是只会那几个转圈圈。这篇文章我准备把这批动画的底细拆开讲清楚它们常见的长什么样、核心动效是怎么写的、选型时该看哪些维度、怎么封装成一套真正能落地切换的组件以及我在实际接入中踩过的坑。适合正在搭前端组件库的同学、被交互细节折磨的页面仔也适合刚学CSS3动画想找练手素材的初学者。1. 先说清楚为什么是150款而不是15款1.1 一个loading背后是几类完全不同的设计语言很多人觉得loading动画就是“转圈圈”其实不是。你把150款放在一起扫一遍会发现它们之间的差异比想象中大得多。有的走纯几何路线用几个色块做旋转和缩放有的走拟物路线模仿沙漏、进度条、刷新箭头有的走文字路线把字母拆开做逐个位移还有一类是骨架屏思路用背景渐变模拟内容加载前的轮廓。选错风格比没有动画更尴尬给一个严肃的医疗后台配弹跳气泡用户体验会非常跳戏。我收藏的这150款里按实现成本大致能分成三档最基础的只用单个元素加一两个 keyframes适合按钮内部的小菊花中档的需要三五个子元素配合不同延迟适合页面级loading高档的会结合裁剪、滤镜、3D变换甚至两步动画串联能承载品牌感很强的开场动效。整理这批素材时我给自己定了一个原则不只看“好看不好看”还要看“实现方式能不能让我说得出口”。如果一个动画效果非常炫但代码写得又长又绕放到团队项目里别人接手困难我会把它归入“仅欣赏”而不是“可复用”。1.2 150款的真正价值建立选型直觉收藏1000个资源不如真正理解100个的实现思路。前面说“150款不是收藏数量的炫耀”是因为当你把这么多样本摆在一起横向对比会自然形成一套自己的选型直觉。举例来说看多了你会发现转圈类loading中border实现的双半圆环比伪元素拼出的圆环更容易控制粗细和颜色点阵类loading中用“透明度延迟”实现的呼吸效果比“位移透明度”同时变化的效果更省GPU资源骨架屏类loading中用background-position做扫光比逐帧替换背景图性能好一个量级。这些判断如果只盯着三五个轮子看是永远总结不出来的。另一个隐藏价值是版权和可信度。很多现成的loading仓库要么体积过大要么License不明确而自己维护一个经过筛选的CSS组件集合校验来源、裁剪样式、统一命名放到内部项目里完全没有后顾之忧。150款的规模刚好能覆盖绝大多数业务场景也不至于多到维护不过来。1.3 这份合集适合谁说实话如果你只想要一个能跑的loading效果去CodePen搜“spinner”就够了。但如果你需要的是批量化接入、风格统一、可复用可解释的方案那么理解这批动画的构成规律会更值钱。适合三类人第一类是前端工程师尤其要做中后台系统、H5活动页、小程序组件需要根据业务快速切换loading形态第二类是UI/UX设计师需要验证自己设计稿里的loading动效有没有更好的实现方式避免“设计很丰满、CSS很骨感”第三类是刚接触CSS3动画的新手从这150款里挑几个经典开始拆解比啃整本CSS书来得快。说白了这是一篇“看着炫拆开更香”的源码级解读。2. 拆开看看一款CSS3 loading动画到底由什么组成2.1 静态骨架一个容器和若干子元素所有CSS loading动画无论看起来多复杂静态结构都离不开“一个容器 若干可复位的子元素”。容器负责定位和尺寸约束子元素负责被动画属性驱动。经典的点阵loadingHTML通常是3到5个span转圈loading往往只需要div加上border边框波浪loading则可能需要多个块级元素叠在不同高度上。我建议把“子元素数量”当作一个重要的选型指标。元素越多视觉越丰富但DOM开销和CSS代码量也会上升在低端机上更容易出现卡顿。150款素材里单一元素能做到惊艳的是少数最典型的做法是用border-radius和transform组合出各类形态。如果你想在项目里减少DOM嵌套优先挑这类“单节点方案”。2.2 动效实现animation 与 keyframes 的配合核心代码永远是这两条keyframes定义关键帧animation属性把时间轴挂到元素上。一个旋转loading的CSS可能短到十行以内.spinner { width: 40px; height: 40px; border: 4px solid rgba(0, 0, 0, 0.1); border-top-color: #3498db; border-radius: 50%; animation: spin 0.8s linear infinite; } keyframes spin { to { transform: rotate(360deg); } }很多新手以为keyframes必须写大段百分比其实loading场景里from和to两个关键帧往往就够。150款动画里真正迷人的部分更多是多个元素设置了不同的animation-delay让它们依次启动形成视觉上的“波浪”或“错落”效果。比如三颗小球依次弹跳就是给第2、第3颗球分别加上0.15s和0.3s的延迟。2.3 形变转换transform 中 rotate / scale / translate 怎么用CSS3里最常用的三个形变函数恰好对应三种最主流的loading形态。rotate转圈类。适合表达“处理中”“刷新中”。性能消耗低因为它能命中GPU合成层。scale缩放类。适合表达“加载占比”“进度反馈”。CSS3的scale缩放方案在loading里最常见的用法是让圆形区域像呼吸一样由小到大、再由大到小。注意缩放最好不要单独作用于整个容器否则容器里的文字会跟着一起变形正确做法是把缩放作用于内部装饰层。translate位移类。适合模拟小球碰撞、字母跳动、进度条滑动。translate和opacity组合出现的概率非常高因为位移结合透明度淡入淡出会让元素移动不生硬。这三类形变可以任意组合比如“位移缩放”“旋转透明度渐变”。写代码时有个经验loading动画里尽量只变化transform和opacity不要动width、height、margin这些布局属性。前者走合成器后者会强制重排性能差距在低端手机上能差出十倍。2.4 缓动与节奏easing 决定“手感”同样30帧的动画linear和cubic-bezier呈现出的“手感”完全不同。线性旋转适合雷达、齿轮这类匀速机械感弹跳小球则适合cubic-bezier(0.36, 0.07, 0.19, 0.97)这类先快后慢曲线呼吸缩放更适合ease-in-out让节点在两头减速。150款素材中有相当一部分的“高级感”就来自缓动。仔细观察会发现很多炫酷效果的keyframes其实很普通只是中间巧妙设置了一个反向关键帧。例如keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.85); opacity: 0.5; } }这种“两端相同、中间不同”的写法配合ease-in-out会让整个动画循环得特别顺。做loading选缓动时我建议不要直接套默认值依次试一下linear、ease-in-out、cubic-bezier三档只用5分钟效果差别非常明显。3. 150款loading的分类选型别再大海捞针3.1 按视觉形态分圆环、点阵、骨架屏、文字、混合整理这批素材时我按视觉形态把它们分成了五类分类表如下形态代表实现适用场景性能难度圆环类border圆环、SVG circle描边全局加载、按钮内嵌高低点阵类多个点缩放/透明度错落卡片局部加载中低骨架屏灰色背景渐变扫光列表、图片占位中中文字类字母依次位移/翻转品牌加载页高中混合类多个动态元素组合引导页、首屏动画低中高这种分类的价值在于你不需要在150个文件里挨个翻看到“页面级、品牌感、需要撑住首屏”这些关键词就直接定位到混合类和文字类看到“接口请求按钮、表单提交中”就只看圆环类。我做内部组件库时也按这套分类建目录后续任何人进来都能快速找到需要的loading。3.2 按使用场景选型首屏、按钮、局部容器、图片占位同一个loading放在不同位置观感完全不同。选型时先问自己在哪个场景而不是先看动画好不好看。首屏加载空间大、展示时间长适合骨架屏或带品牌标识的混合动效。这类动画不能太闹尽量保持页面整体色调统一背景用浅色渐变动画集中在视觉焦点。按钮加载空间很小只允许一两秒内的微动效圆环类最合适。这时有个细节按钮里的loading必须和文字文案互斥点击后按钮应进入disabled状态把原来的文字替换成loading动画尺寸控制在按钮高度的60%以内。我一般用CSS变量区分三种按钮尺寸.btn .spinner { width: calc(var(--btn-height) * 0.4); height: calc(var(--btn-height) * 0.4); border-width: calc(var(--btn-height) * 0.05); }局部容器比如表格刷新、面板重载点阵类和细圆环类都不错。不建议用大骨架屏那样会让局部区域看起来像整页白屏。图片占位骨架屏是主流方案因为它能预告内容的轮廓减少用户等待的焦虑感。3.3 配速与配色经验值直接抄先给一组我多年实际项目中验证过的参数直接抄不会有大的偏差。项目推荐值理由单圈旋转时长0.6s - 1s低于0.5s显急促高于1.2s显迟钝点阵错落延迟0.1s - 0.2s低于0.1s像一起动高于0.25s像排队loading主色品牌色或灰色系不要用高饱和对比色作为主体背景遮罩透明度rgba(0,0,0,0.35) 左右太高压抑太低看不到loading动画常驻时长3s内结束最好超过3s需要补充进度文字或重试入口配色方面千万别让整个loading由纯黑和大红组成那会让用户觉得页面报错了。150款素材里面视觉最舒服的那批基本都符合“一个亮色点 若干浅色装饰”的规律。想做出质感把主色饱和度降低5%-10%页面会立刻温和很多。4. 把150款变成一套可切换的组件4.1 目录组织与命名规范有了一堆CSS文件直接扔到项目里早晚会乱。我常用的组织方式是按“形态-名称”命名目录loading-animations/ ├── spinner-ring/ │ ├── ring-1.html │ ├── ring-1.css │ ├── ring-2.html │ └── ring-2.css ├── dots/ │ ├── dots-1.html │ ├── dots-1.css │ └── ... ├── skeleton/ ├── text/ └── mixed/每个动画的HTML和CSS尽量独立成文件。这样既方便预览也方便后续转成组件。如果做成组件库我建议暴露一个统一的注册表export const loadingRegistry { ring1: { html: ..., css: ... }, dots1: { html: ..., css: ... }, skeletonCard: { html: ..., css: ... }, };注册表的好处是组件可以按key加载对应的模板和样式实现真正的按需引用而不是把所有动画全量打进bundle。4.2 统一容器HTML结构如何承载不同动画无论动画内部长什么样对外暴露的统一结构建议做成这样div classui-loading>.ui-loading__inner { width: var(--ui-loading-size); height: var(--ui-loading-size); position: relative; display: inline-block; }这样无论内部是圆环、点阵还是文字尺寸都受同一个CSS变量控制。vBird里看到的那些“只换动画名其他不变”的组件原理就是这个。4.3 CSS变量做主题化配置主题化是组件能不能推广开的关键。150款动画如果要手动改颜色人会疯。用CSS变量可以一次性搞定:root { --loading-primary: #3498db; --loading-secondary: rgba(52, 152, 219, 0.2); --loading-bg: rgba(255, 255, 255, 0.9); --loading-speed: 0.8s; } .ui-loading__inner { border-top-color: var(--loading-primary); border-color: var(--loading-secondary); }动画中的每一个颜色、时长、尺寸都尽量抽成变量后面你会感谢自己。比如项目A要求loading用品牌绿色项目B要求Loading用天空蓝只需要在入口处改两个变量组件代码一行不动。同理速度变量可以直接控制用户感知的“轻快/沉稳”深色模式还能通过变量的覆盖自动切换。4.4 按需加载与动画状态管理做单页应用时全量引入150个动画会让JS体积白白增加不少。好的做法是所有loading组件懒加载只在某个动画被使用时才注入对应CSS。现代框架里最容易落地的是CSS Module或者CSS-in-JS的按需方案。以React为例可以封装一个组件根据variant动态importfunction Loading({ variant ring1, size 32 }) { const [style, setStyle] useState(null); useEffect(() { import(../loading-styles/${variant}.css).then(() setStyle(true)); }, [variant]); return div classNameui-loading style{{ width: size, height: size }}{/* ... */}/div; }状态管理还要考虑一件事同一个页面上多个loading同时出现时不能让所有动画都从零开始跑。可以用一个全局的“loading上下文”记录当前页面的加载状态当切换路由或关闭弹窗时统一移除挂载节点并清除动画class避免僵尸动画一直空转。这个小细节很多组件库都没处理好我自己踩过一次——弹窗关了背后loading还在转排查半天才发现是忘记清定时器。5. 接入实战从素材到生产环境的常见问题5.1 动画不动的诊断清单收藏的150款动画往项目里搬时“动画不动”是最高频问题。我列了一份排查清单按顺序过一遍能解决九成情况检查元素是否能被CSS选中类名冲突、选择器层级、CSS文件有没有被正确引入。检查元素尺寸是否为0border实现了圆环loading若容器宽高是0边框不显示把width和height临时加到100px验证。检查transform是否被覆盖项目里通用样式如果写了transform: translateZ(0)提性能可能会覆盖关键帧里的transform: rotate(360deg)。解决方案是用animation-composition或把旋转挂到子元素上。检查prefers-reduced-motion一些系统开启“减少动态效果”后浏览器会限制CSS动画可以用代码兜底显示静态loading。检查浏览器兼容低版本内核支持度有限后面专门列兼容表。5.2 页面卡顿与GPU加速Loading动画虽然简单但如果是全局弹层、满屏刷新依然可能拖垮低端机。卡顿通常不是单个动画性能差而是同时有过多元素在做复合动画。排查手段有两个打开DevTools的Performance面板录制一段loading看动画片段里的Scripting和Rendering用时再开启Rendering面板的“Layer borders”观察有没有大量黄色图层。出现卡顿后优先做三件事把width、height、margin等布局属性替换成transform中的scale和translate。给动画元素加上will-change: transform用完记得移除否则内存占用会一直很高。减少同时变化的元素数量点阵loading从8个点降到5个点视觉上几乎无差别。5.3 与其他CSS框架的冲突处理很多项目配套了Tailwind、Element、Antd动画不动可能是因为框架级样式影响了子元素的定位方式。最常见的冲突有四个冲突来源现象解决方案reset.css覆盖border样式border圆环完全消失在组件类名下重写borderflex导致容器尺寸异常点阵全部挤到一行给inner加固定宽高全局transition干扰动画出现延迟或过渡对inner单独设置transition: none全局transform属性关键帧rotate被覆盖给动画元素加更具体的选择器如果项目UI框架自带transition且作用于所有后代元素我习惯在loading容器上加一句.ui-loading__inner, .ui-loading__inner * { transition: none !important; animation: none !important; }这里不是推荐滥用!important但loading动画往往需要在框架重压下“保命”这对生产环境是必要的兜底。5.4 兼容性一张表直接对照CSS3动画主流浏览器支持已经很成熟但是在老项目、特定浏览器环境里还是要先看兼容基线特性ChromeFirefoxSafariIE/Edge旧内核transform支持支持支持IE10animation支持支持支持IE10CSS变量支持支持支持 9.1不支持prefers-reduced-motion支持支持支持不支持backdrop-filter部分支持支持不支持如果你的产品需要兼容IE11CSS变量方案就需要换成SCSS变量编译或者直接在组件里写死颜色。另外animation-delay负数是一个很好用的技巧把delay设为负值会让动画直接跳到时间轴中间多个loading同时引入时它能让动画看起来“已经在跑”而不是齐刷刷从第一帧启动。这点在首屏多组件加载时非常实用。6. 三个经典loading从零手写6.1 旋转圆环最快落地的一款单元素实现前面已经贴过基础版。这里我给一个带磨砂遮罩的完整版适合页面级全局loadingdiv classloading-mask div classspinner-ring/div /div.loading-mask { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.75); backdrop-filter: blur(4px); z-index: 9999; } .spinner-ring { width: 48px; height: 48px; border: 5px solid rgba(52, 152, 219, 0.15); border-top-color: rgba(52, 152, 219, 0.9); border-radius: 50%; animation: ring-rotate 0.8s linear infinite; } keyframes ring-rotate { to { transform: rotate(360deg); } }有几个细节值得展开。用inset: 0代替top/left/right/bottom: 0能让代码更整洁border-top-color单独设置彩色比其他三条边框全彩更耐看视觉重心更集中animation-delay不需要设置因为旋转是连续循环的任何时候插入都是“已在转”的状态。如果想做双色环再加一层伪元素错开旋转方向即可。6.2 三点呼吸局部加载最常用按钮局部加载建议用点阵。以三个点为例HTML结构保持三个spanCSS里利用nth-child控制延迟div classdots-loading span/span span/span span/span /div.dots-loading { display: flex; gap: 6px; } .dots-loading span { width: 8px; height: 8px; border-radius: 50%; background: var(--loading-primary, #3498db); animation: dot-breathe 1.2s ease-in-out infinite; } .dots-loading span:nth-child(2) { animation-delay: 0.2s; } .dots-loading span:nth-child(3) { animation-delay: 0.4s; } keyframes dot-breathe { 0%, 100% { transform: scale(0.8); opacity: 0.5; } 50% { transform: scale(1.1); opacity: 1; } }这里使用的“缩放透明度”双属性组合比单纯位移更柔和。gap设置成6px左右点之间不会拥挤。如果你在按钮里用记得把span尺寸改成按钮高度的40%并把间距同步缩小否则三个点会把按钮文案挤爆。6.3 骨架屏从列表到卡片都能套骨架屏不属于典型的旋转loading但用户体验最佳。以一张商品卡片为例div classskeleton-card div classskeleton-block image/div div classskeleton-line title/div div classskeleton-line price/div /div.skeleton-card { padding: 16px; border-radius: 12px; background: #fff; } .skeleton-block, .skeleton-line { background: linear-gradient(90deg, #eee 25%, #ddd 37%, #eee 63%); background-size: 400% 100%; animation: skeleton-sweep 1.4s ease infinite; } .skeleton-block.image { height: 120px; border-radius: 8px; margin-bottom: 12px; } .skeleton-line.title { height: 16px; border-radius: 4px; margin-bottom: 10px; } .skeleton-line.price { width: 80px; height: 16px; border-radius: 4px; } keyframes skeleton-sweep { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }核心思路是让渐变背景在元素内部循环移动模拟一道光扫过占位区域。这里用background-size: 400%保证扫光效果有足够的移动空间。骨架屏的动画时间可以比旋转类慢一些1.3s到1.6s都不错扫太快会让人分心。另外骨架屏最好搭配一点微弱的整体透明度变化避免在暗色模式下显得刺眼。7. 我的真实体会和差异化建议7.1 收藏150款之后怎么消化收藏这件事本身没有价值消化才有。面对150款动画我的方法是每周抽出半小时做“动画考古”选三个动画拆解它们的关键帧尝试改一个参数看有什么变化。这样持续一个月你会形成对动画节奏非常敏锐的手感。之后你再看到任何loading会下意识猜它用了几个关键帧、每个子元素延迟多少而不是只会说“好看”。另外我强烈建议每个开发者都维护一个自己的“小样本库”。不需要150款那么多把自己最常用的20款CSS3 loading放进去加上注释为什么用这个、适合什么场景、有没有性能坑。这比收藏夹里堆几百个链接有用得多。面试或评审时这也是一个很好的差异化亮点——大多数人都只是“用过”你能讲清楚“为什么”。7.2 做loading时的心态与设计原则最后分享一个我踩过多次坑后总结的原则loading动画的本质是“让等待变短”。它不是炫技舞台不是把用户注意力全吸走的设计彩蛋。好的loading应该做到存在感适中、风格统一、对后续内容有所呼应。150款素材每款都不是终点而是起点——当你理解了它们的构成和取舍你完全能在此基础上改出自己的品牌版本而不再需要满世界找素材。我个人在实际项目里最常用的反而是最简单的圆环和骨架屏复杂动效一年用不到两三次。但正因为看过了那150款我才能确定简单的就是最优解这就是大规模刷素材给我带来的最大底气。如果你也收藏了一堆“炫酷收藏夹”别让它们吃灰抽个下午逐个打开、拆解、跑一遍你会收获比“会抄代码”多得多的东西。
返回列表