最简纯css转圈动画:用border构造单色圆环+rotate(360deg)无限旋转,配合border-radius:50%、transparent边框及linear缓动;safari需will-change:transform或translateZ(0)启用硬件加速。

用 border + animation 实现最简转圈动画
纯 CSS 实现加载转圈,核心是利用 border 的边框绘制和 transform: rotate() 配合 @keyframes。不需要 js,不依赖外部资源,兼容性好(IE10+)。
常见错误是直接对元素整体旋转,导致内容也跟着转;正确做法是只旋转一个无内容的伪元素或空
border 构造圆环。
- 必须设置
border-radius: 50%让元素呈圆形 - 四边
border宽度一致,但只留一边有颜色(如border-top: 4px solid #007bff),其余设为透明(transparent) - 动画需用
transform: rotate(360deg),不能用rotateZ或其他变体,否则部分旧版 Safari 不识别 - 推荐加
animation-timing-function: linear,避免默认ease导致转速不均
.spinner { width: 20px; height: 20px; border: 4px solid transparent; border-top-color: #007bff; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }
解决 Safari 中 transform 动画卡顿问题
Safari(尤其 ios 14–15)对纯 transform 的 rotate 动画可能掉帧,尤其在低功耗模式下。根本原因是浏览器未将其提升为独立图层。
强制启用硬件加速可缓解:给转圈元素添加 will-change: transform 或补一个极小的 translateZ(0)。
立即学习“前端免费学习笔记(深入)”;
-
will-change: transform最直接,但不要滥用——仅用于持续动画的元素,否则影响内存 - 若需兼容 iOS 12–13,用
transform: rotate(0deg) translateZ(0)开启 GPU 渲染 - 避免同时对父容器设置
overflow: hidden和子元素旋转,Safari 可能截断动画轨迹
.spinner { will-change: transform; /* 或者 */ /* transform: rotate(0deg) translateZ(0); */ }
响应式转圈大小适配不同设备
固定像素(如 20px)在高 DPI 屏幕上显得过小,在小屏手机上又可能遮挡内容。应优先使用相对单位,并配合 min-width/max-width 控制边界。
- 用
em或rem替代px,让尺寸随父文本缩放(例如按钮内的加载图标) - 用
clamp()设置弹性范围:width: clamp(16px, 2.5vw, 32px),兼顾小屏、桌面、大屏 - 注意
border-width也要同比例缩放,否则圆环粗细会失衡 - 如果嵌入文字行内,加
vertical-align: middle避免基线错位
.spinner-inline { width: clamp(12px, 1.8vw, 24px); height: clamp(12px, 1.8vw, 24px); border-width: clamp(2px, 0.9vw, 4px); vertical-align: middle; }
用 clip-path 做更精细的转圈控制(如缺口圆环)
默认 border 转圈是实心圆弧,但有些设计需要“缺口”或“断开”的环形(比如进度感更强的 loading)。这时 clip-path 比改 svg 更轻量。
原理是先画一个完整圆环,再用 clip-path: polygon() 切掉一小段,再旋转它——视觉上就像一段弧在转动。
- 缺口角度建议控制在 15°–30°,太小难察觉,太大像卡住
- 必须配合
transform-origin: center,否则裁剪后旋转中心偏移 - firefox 对
clip-path的polygon支持稳定,但 IE 完全不支持,需降级回 border 方案
.spinner-gap { width: 32px; height: 32px; border: 4px solid #007bff; border-radius: 50%; animation: spin-gap 1.2s linear infinite; transform-origin: center; clip-path: polygon( 0% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 50%, 15% 50%, 15% 45%, 0% 45% ); } @keyframes spin-gap { to { transform: rotate(360deg); } }
实际项目中,最容易被忽略的是动画触发时机与 dom 生命周期的配合——比如组件挂载后才开始动画,或请求结束时及时移除 class。CSS 动画本身没状态管理,这部分得靠 JS 控制 class 切换,别指望纯 CSS 自动启停。