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

用 border + animation 实现最简转圈动画
纯 CSS 实现加载转圈,核心是利用 border 的边框绘制和 transform: rotate() 配合 @keyframes。不需要 JS,不依赖外部资源,兼容性好(IE10+)。
常见错误是直接对元素整体旋转,导致内容也跟着转;正确做法是只旋转一个无内容的伪元素或空 @keyframes spin {
to { transform: rotate(360deg); }
} Safari(尤其 iOS 14–15)对纯 强制启用硬件加速可缓解:给转圈元素添加 固定像素(如 默认 原理是先画一个完整圆环,再用 @keyframes spin-gap {
to { transform: rotate(360deg); }
} 实际项目中,最容易被忽略的是动画触发时机与 DOM 生命周期的配合——比如组件挂载后才开始动画,或请求结束时及时移除 class。CSS 动画本身没状态管理,这部分得靠 JS 控制 class 切换,别指望纯 CSS 自动启停。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;
}
解决 Safari 中
transform 动画卡顿问题
transform 的 rotate 动画可能掉帧,尤其在低功耗模式下。根本原因是浏览器未将其提升为独立图层。will-change: transform 或补一个极小的 translateZ(0)。
will-change: transform 最直接,但不要滥用——仅用于持续动画的元素,否则影响内存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() 切掉一小段,再旋转它——视觉上就像一段弧在转动。
transform-origin: center,否则裁剪后旋转中心偏移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%
);
}










