仿招商银行Loading动效:一套可复用的CSS动画方案 简介一份面向Android开发者的自定义加载动画实现仿照招商银行App中的loading效果通过自定义Dialog配合旋转动画完成主要解决应用内等待场景的视觉优化问题。压缩包共1465个文件包含612个png图片资源、561个xml布局与动画配置、114个json数据、108个class编译文件、8个java源码及11个jar库等整体约21.06MB附有完整Android工程结构和可运行的app-debug.apk便于直接导入Android Studio学习。目前已有8889人学习下载适合初中级Android开发者了解自定义Dialog、主题设置和旋转动画的应用。资源内提供布局文件、Dialog主题、旋转动画等完整代码读者可对照工程结构快速定位关键实现也可以直接运行apk查看效果为类似加载动画需求提供可复用的参考模板。 仿招商银行loading把银行App的开屏动效拆成一套可复用的CSS动画方案做前端这些年我有个改不掉的毛病每次打开一个体验做得好的App第一反应不是看内容而是盯着loading动画看。尤其是银行类产品像招商银行的App加载动画做得是真讲究——不光是好看它能在短短一两秒内把品牌调性、产品气质全部传递出来。所以很多前端同行都会拿“仿招商银行loading”当练习项目我前阵子也完整复刻了一版从拆解动效到落地实现踩了不少坑也积累了一套可以复用的思路。这篇就完整记录一下我是怎么把那个loading“拆开”再“装回去”的。不管你用的是Vue、React还是纯静态页面这套CSS动画方案都直接能抄作业。1. 银行级loading动画到底“难”在哪里先说个反直觉的结论仿招商银行loading真正难的不是动画本身而是“拆层”和“对节奏”。我见过有人用Canvas一帧帧去绘制也有人直接找设计要了Lottie动画文件但这两种方式在这个场景下都不够优雅。为什么因为银行App的loading是有明确的层级结构和节奏设计的每一步都有它的功能性——品牌展示、进度反馈、注意力承接全挤在1.5秒左右的时间里完成。1.1 视觉拆解一次loading里其实藏了四层动效我反复看了招商银行App的加载过程把它的视觉构成拆成四层背景层通常是白色或非常浅的底色营造干净的感觉。旋转环层围绕中心标志旋转的圆环一眼看过去是“正在加载”的直觉信号。品牌标志层中间的燕子形标志或者“招商银行”的图形化标志它会在加载过程中做缩放、渐显。文字层“招商银行”四个字以及下面的英文或slogan通常是最后出现的。四层不是同时运动而是有先后顺序的。这个顺序就是我说的“节奏”。很多仿写失败不是因为动画写不出来而是四层动效挤在一起眼睛看过去就是一顿乱动没有任何呼吸感。1.2 那到底是靠什么撑起“高级感”的真正让我觉得值得仿写的是它处理细节的方式。举个例子旋转环不是全部可见的它有一段是渐隐的看起来就像光在绕圈扫过标志不是直接怼出来而是先轻微缩小再弹回原尺寸带一点弹性文字不是整体淡入而是带一点上移的位移。这些细节叠加起来才是“银行级”质感的核心。所以我当时定了一个目标不依赖任何动效库用纯CSSSVG把这四层全部实现出来。这样不仅可控而且方便调整节奏也方便后续集成到任意前端项目里。2. 还原前先做逆向工程把加载过程排成一条时间轴动手写代码之前我做了一件事把招商银行loading完整播放过程录下来然后逐帧去看。这步特别重要因为光凭肉眼感觉去“差不多写一个”最终出来的效果一定不对味。2.1 把1.5秒拆成四个时间区间我的原始版本加载总时长大约是1.5秒我把它拆成了四个阶段时间段比例画面内容0 - 0.25s0% - 16%旋转环出现从0度开始旋转0.25s - 0.6s16% - 40%旋转环持续转动标志开始放大显现0.6s - 0.9s40% - 60%标志完成弹性缩放旋转环转至接近一圈0.9s - 1.4s60% - 93%“招商银行”文字淡入并轻微上移1.4s - 1.5s93% - 100%整体定住等待页面切换这个时间排布的逻辑在于先用旋转环抓住用户的注意力再让品牌标志“接力”成为视觉焦点最后用文字完成品牌传达。每一阶段都只有一个核心动作不会出现“标志在动的同时文字也在飘”这种信息打架的情况。2.2 为什么要用“图层思维”而不是一锅炖我刚开始写的时候犯过一个错想把所有动画放在一个元素上搞定结果CSS写了一堆改起来极其痛苦。后来我想明白一个事——每个视觉元素就应该是一个独立的DOM节点动画各管各的通过animation-delay来控制先后顺序。这个思路其实跟设计软件里的图层是一模一样的。拆成独立图层还有一个好处后续如果要加“加载百分比”这种功能只需要在旋转环那层做修改完全不影响其他层。这种可维护性对于实战项目来说才是最重要的。3. 从零手写HTML结构、SVG与CSS关键帧目标定了时间轴也排好了现在开始真正写代码。这个版本我做了简化但核心的动效逻辑完整保留直接复制到本地就能跑。3.1 HTML骨架四个节点各司其职先看HTML结构div classcmb-loader !-- 旋转环层 -- div classloader-ring/div !-- 标志层SVG燕子 -- div classloader-logo svg viewBox0 0 120 120 xmlnshttp://www.w3.org/2000/svg circle classring-progress cx60 cy60 r48 fillnone stroke#e4002b stroke-width5 stroke-linecapround stroke-dasharray301.6 stroke-dashoffset301.6 transformrotate(-90 60 60) / path classbird-body dM60,28 C75,28 86,38 86,52 L60,57 L34,52 C34,38 45,28 60,28 Z fill#e4002b / path classbird-tail dM60,57 L53,70 L60,63 L67,70 L60,57 Z fill#e4002b / /svg /div !-- 文字层 -- div classloader-text span招商银行/span spanCMB CHINA/span /div /div这里说几个选型的细节。第一旋转环我没有用额外的div加border去做而是直接用SVG的circle加stroke-dasharray和stroke-dashoffset。这样做的好处是环的“缺口”大小可以精确控制而且能很方便地做成渐变描边。如果只用CSS border想做出“一段有颜色、一段透明”的效果就得动用conic-gradient麻烦且兼容性没有SVG好。第二燕子的SVG路径是我简化过的。它在原版基础上把翅膀收敛了一些视觉上更接近招行的标志轮廓但不完全是官方矢量图。如果你要商用或做高保真还原一定要找设计同学要原始的SVG路径文件直接把path替换就行。3.2 CSS关键帧让四层动效依次苏醒接下来是核心的CSS实现。我按图层写四个关键帧动画并分别控制它们的延迟和持续时间。/* 基础容器 */ .cmb-loader { width: 100%; height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background: #ffffff; } /* 1. 旋转环动画 */ .ring-progress { animation: cmb-ring-spin 1.4s linear forwards; } keyframes cmb-ring-spin { 0% { stroke-dashoffset: 301.6; transform: rotate(0deg); transform-origin: 60px 60px; } 60% { stroke-dashoffset: 120; } 100% { stroke-dashoffset: 0; transform: rotate(360deg); transform-origin: 60px 60px; } }这里有个关键点circle加transform-origin时因为SVG里的坐标体系和普通DOM不同直接用transform-origin: center很容易跑偏。我在SVG里给circle设了transformrotate(-90 60 60)让起始点在正上方动画里再配合transform-origin: 60px 60px去旋转这样圆心才稳定。再往下是标志层的弹性动画/* 2. 标志弹性显现 */ .bird-body, .bird-tail { animation: cmb-bird-pop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.25s both; } keyframes cmb-bird-pop { 0% { opacity: 0; transform: scale(0.4); } 60% { opacity: 1; transform: scale(1.08); } 100% { opacity: 1; transform: scale(1); } }cubic-bezier(0.34, 1.56, 0.64, 1)是回弹效果的灵魂。它的特点是末段曲线值超过1会让元素先冲过正常尺寸再回弹到1视觉上就是“弹了一下”。很多人做弹性动画喜欢用bounce之类的内置函数但那个回弹次数太多显得很卡通用在银行类产品上不够稳重。这个贝塞尔曲线只弹一次干脆利落刚好符合银行气质。最后是文字层的进场动画/* 3. 文字上浮淡入 */ .loader-text { margin-top: 24px; text-align: center; } .loader-text span { display: block; opacity: 0; } .loader-text span:first-child { font-size: 22px; color: #e4002b; letter-spacing: 8px; font-weight: 600; animation: cmb-text-rise 0.5s ease-out 0.9s forwards; } .loader-text span:last-child { margin-top: 6px; font-size: 11px; color: #999; letter-spacing: 3px; animation: cmb-text-rise 0.5s ease-out 1.1s forwards; } keyframes cmb-text-rise { 0% { opacity: 0; transform: translateY(12px); } 100% { opacity: 1; transform: translateY(0); } }文字层我用的是translateY做位移配合opacity淡入。这两个属性都是GPU加速的合成属性动画过程中不会触发布局和重绘性能上远比直接改top值要好。后面第五部分我会专门讲性能问题这里先有个概念。3.3 为什么关键帧的“结束状态”要用forwards和both写动画时有个特别容易忽略的细节animation的填充模式。很多新手写淡入动画元素最后确实出现了但过程是“先显示再消失再出现”因为关键帧默认只在动画执行期间生效动画结束就恢复初始状态。我在三个动画结尾都加上了forwards或both。用both的好处是动画延迟执行的那段时间里元素就已经应用了0%关键帧的状态比如opacity为0不会出现闪烁forwards则是让动画结束后的状态保留下来。这个细节是保证动画“稳定”的关键尤其当页面切换和动画结束时间很接近时少了它极容易出现loading已经结束、文字却闪了一下才出现的bug。4. 时序编排让四层动效像同一个动作的连续过程代码写完了但如果你直接把上面的CSS拿去跑会发现效果还是差点意思。为什么因为四层动画之间缺了“衔接感”。单独看每一层都动得没问题合在一起就感觉各动各的。这就是时序编排的功夫。4.1 谁先谁后遵循“先环后标再文字”的引导逻辑我前面列的时间轴里旋转环是0秒启动标志是0.25秒延迟文字是0.9秒延迟。这个设计不是拍脑袋定的背后的逻辑是0 - 0.25秒旋转环先动起来建立“加载中”的心理暗示让用户知道系统在工作。0.25秒时环已经转了将近60度用户的目光被稳定吸引在中心区域此时标志突然放大显现注意力会自然地从“环”切换到“标志”。0.9秒后标志已经完全稳定环的进度也接近走完一圈这时再出现文字完成品牌信息的传递。从导览心理学的角度看这就像一个路标系统先是显眼的指示牌旋转环然后是指示牌的Logo标志最后是详细地址文字。每一步都在上一步的基础上叠加而不是分散用户的注意力。4.2 关键帧时长比例总动画时长和进度感知的关系还有一个细节是旋转环的strok-dashoffset变化和时间的对应关系。我设置的是0%时offset为301.6即完整周长环不可见100%时offset为0环闭合。但中间不是匀速的60%的时候已经走到了120意味着前半段进度感很强后半段变缓。这模拟了“下载进度”的常见心理模型开始快、结束慢。如果反过来——前半段慢吞吞、后半段突然加速——用户会觉得卡顿哪怕动画实际总时长是一样的。这个小细节是我反复对比原版视频才发现的原版里的环形进度确实不是严格匀速的。4.3 完整代码怎么组织更合理上面的CSS片段分散在各节里实际项目里建议把loading相关样式合并成一个文件用CSS变量把关键数值抽出来方便统一微调.cmb-loader { --cmb-red: #e4002b; --cmb-duration-ring: 1.4s; --cmb-delay-logo: 0.25s; --cmb-delay-text-1: 0.9s; --cmb-delay-text-2: 1.1s; /* ...其余样式 */ }这样调试节奏时不用翻到每个关键帧里去改数字直接在变量区域微调即可。我实际开发时把0.25秒改到0.2秒、把1.4秒改到1.2秒都是通过改变量一键完成的非常方便。5. 性能与兼容性loading动画也不能“卡一下”很多前端新手不知道一个冷知识CSS动画的性能差异可能达到几十倍。有的属性一改浏览器就要重新计算整个页面的布局有的属性改起来浏览器轻松地把图层丢给GPU做合成。loading动画作为用户看到的第一帧一旦出现卡顿用户对App整体的第一印象直接崩掉。5.1 只用transform和opacity别去碰layout属性我这次实现的动画里位移全部用translateX/translateY缩放用scale渐显用opacity旋转用rotate。这四个属性都属于合成属性不会触发浏览器的layout布局和paint绘制只会触发composite合成。GPU处理合成极快即使在中低端手机上也能保持60帧。反过来如果要动画的属性是width、height、top、left、margin这类layout属性浏览器每一帧都要重新计算布局代价极其昂贵。举个例子文字上浮动画如果写成/* 千万别这么写 */ keyframes bad-example { from { top: 20px; opacity: 0; } to { top: 0; opacity: 1; } }这个动画会让浏览器每一帧都去计算元素的新位置然后重新布局。虽然文字上浮只有12像素但在低端机器上动画越复杂、元素越多卡顿越明显。同样的效果用translateY实现几乎零开销。5.2 will-change的正确用法和滥用的后果我做性能优化时习惯性地给动画元素加will-change: transform来主动提醒浏览器提前优化但这里必须提醒一句will-change不是银弹加多了反而更卡。因为它会为每个元素新建一个合成层合成层太多会占用大量GPU内存移动端很容易爆。正确姿势是给动画元素先不加让浏览器自己判断如果真观察到掉帧再加到“正在进行动画的那个元素”上。我的经验是这个loading场景只有旋转环值得加will-change其余层动画很轻浏览器足够应付。5.3 兼容性处理和降级方案CSS动画的兼容性整体已经很好了但还是要处理两类情况旧浏览器不支持cubic-bezier(0.34, 1.56, 0.64, 1)这种情况很少见但一旦出现回弹效果会丢失动画会直接跳到终点状态。兜底方案是在关键帧里加一个接近终点的中间态保证至少有淡入效果。用户开启了“减少动态效果”优秀的页面要对这类用户友好。用prefers-reduced-motion可以检测检测到就关闭所有动画直接展示最终状态。media (prefers-reduced-motion: reduce) { .cmb-loader * { animation: none !important; opacity: 1 !important; } }这段降级代码我每次做动效都会带上。银行App面向的用户群体包括大量对动效不敏感甚至会产生眩晕的老人这是基本的人文关怀也是专业团队会注意到的细节。6. 复刻过程中的几个坑和后续可扩展的方向这套仿招商银行loading的CSS方案我一共写了三版才稳定。前两版都有各自的问题我复盘后整理了三个最有代表性的坑对想拿这个项目练手的朋友应该很有帮助。6.1 SVG的transform-origin搞不定旋转环乱跑第一版我把旋转环做成div加border旋转中心倒是好办但做不出“只有一段弧可见”的效果。换成SVG circle后又被transform-origin坑了一回——在SVG里设置transform-origin: center结果环绕着SVG画布的左上角旋转完全看不出是“绕中心转圈”。后来才发现SVG的transform是受viewBox坐标系影响的而且circle本身的原点默认在SVG画布原点(0,0)不是图形自身中心。解决办法就是给circle属性上直接写transformrotate(-90 60 60)把这作为初始旋转然后在CSS关键帧里再用rotate(360deg)叠加旋转。这样圆心就被固定在了(60, 60)这个viewBox坐标上转得稳稳的。6.2 动画帧率测试用肉眼不如用DevTools有一版我觉得动画已经很顺了但在朋友的安卓中端机上跑起来明显一卡一顿。肉眼不太容易定位问题后来我打开Chrome DevTools的Rendering面板勾选了FPS meter又切到Performance面板录制了一段才发现是文字的opacity动画触发了一个大面积重绘。原因是我给文字层加了一层细微的box-shadow阴影阴影渐变区域在opacity变化时被反复重绘。去掉阴影后帧率立刻满血。所以想测真实性能别用MacBook上的Chrome测要找一台性能一般的安卓机实测或者直接用DevTools的CPU降频功能模拟低端设备。6.3 循环播放还是只播一次取决于应用场景我这个版本做的是“单次播放”因为App开屏loading本来就是播完一次就切页面。但如果你是把这套动效复用在网页版的全局加载框里那就是循环播放的场景需要处理一些循环细节旋转环的stroke-dashoffset要先归位再继续转否则每次循环都会“顿”一下。可以用无限循环的方式去给旋转环单独做动画而标志和文字只播放一次这样加载框可以一直转品牌元素也不会反复弹跳刷存在感。6.4 后续扩展方向从“仿”到“有自己的东西”最后一个建议是仿写这个项目练手没问题但仿完之后一定要试着自己改一版。换品牌色、换标志图形、调整节奏曲线甚至把旋转环改成“扫光”效果、把标志改成走马灯式轮播都可以。我做完这套之后顺手给它加了一个加载进度上报的功能——真实加载进度到达关键节点比如50%、80%时让旋转环的弧度和实际进度关联这样“loading”就不再是纯装饰而是具备真实信息传达功能的组件。说到底仿招商银行loading的价值不在于“仿得有多像”而在于你把那个过程里的设计逻辑和技术细节都啃透了下次遇到任何复杂的动效需求都能用同样的方法去拆解、去实现。这波投入绝对不亏。本文还有配套的精品资源点击获取