stroke-dasharray 必须设为“周长, 0”且周长需动态获取:先等 dom 就绪并 requestanimationframe 后调用 gettotallength(),再设置 css 变量;动画仅通过 stroke-dashoffset 从周长减至 0 实现,并配合 rotate(-90deg) 校准起点至顶部。

stroke-dasharray 值必须等于真实周长,不能手算硬写
很多人直接写 stroke-dasharray: 314,假设 r="50" 就一定对应周长 314.16 —— 但 SVG 经过 transform: scale()、viewBox 缩放或父容器宽高不等时,实际渲染半径已变,硬算值会漂移。更糟的是,若 SVG 还没 layout 完就调用 getTotalLength(),会返回 0。
稳妥做法是等 DOM 就绪后延迟一帧再读:
const circle = document.querySelector('circle');
requestAnimationFrame(() => {
const circumference = circle.getTotalLength();
circle.style.setProperty('--circumference', circumference);
});
-
viewBox="0 0 100 100"且r="50"时,getTotalLength()返回 ≈ 314.16,但 preserveAspectRatio 或 CSS width/height 不一致时仍可能偏差 - 别用
calc(2 * 3.1416 * var(--r))在 CSS 里算周长 ——var(--r)若带单位(如50px),CSS 会报错 - 如果必须纯 CSS(无 JS),至少确保
viewBox和r匹配,并四舍五入取整(如283而非282.74)
stroke-dasharray 必须写两个值,比如 "283, 0"
只写 stroke-dasharray: 283,浏览器会自动补全为 283, 283,结果变成虚线环:实线 283 + 空白 283,动画一动就“闪一下”或卡在半圈。
正确写法是显式声明实线段和空白段长度:
-
stroke-dasharray: 283, 0→ 实线画满整圈,后面不留空,进度条才能连续露出 -
stroke-dasharray: 141.5, 141.5会生成均匀虚线,不适合进度条 - 若用 CSS 变量,务必保证变量值是纯数字(如
--circumference: 283),然后写成stroke-dasharray: var(--circumference), 0
@keyframes 必须只动 stroke-dashoffset,且方向不能反
动画本质是“挪起点”,不是“改长度”。常见错误是只改 stroke-dasharray,或者把 stroke-dashoffset 从 0 动到 283 —— 那是倒着收回去,看起来像动画倒放。
正确逻辑:
- 进度 0% →
stroke-dashoffset: 283(整圈被盖住) - 进度 100% →
stroke-dashoffset: 0(整圈可见) - 动画必须从大值减到小值:
from { stroke-dashoffset: 283; } to { stroke-dashoffset: 0; } - IE 不支持
stroke-dashoffset的 CSS 动画,需 JS fallback 或放弃支持
起始点默认在 3 点钟方向,要对齐顶部得 rotate(-90deg)
SVG <circle></circle> 默认绘制起点是 x 轴正向(3 点钟方向),顺时针画圆。所以原生动画是从右往左“长出来”,移动端用户会觉得违和。
解决方案是加一层旋转校准:
- 给
<circle></circle>加transform: rotate(-90deg),把起点扳到 12 点钟方向 - 注意:这个
transform要写在 circle 元素上,不是父容器;否则stroke-dashoffset偏移基准会乱 - 别用
rotate(75%)—— 单位非法,浏览器静默失败,进度不动;必须用deg,如rotate(-90deg)
关键点藏在细节里:周长不准,整个动画偏;stroke-dasharray 少写一个 , 0,就会闪;rotate 没加或单位错,起点就歪。这些都不是“大概能跑”,而是“差一点就完全不对”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











