先说一个核心判断:Less 作为编译时的预处理器,所有 @progress 变量在构建阶段就被固化成静态 CSS。换句话说,你改一个变量值、刷新页面,并不会“动起来”——它只是重新生成了一套新样式。想让环形进度条随 JS 数据变化而旋转填充,必须靠 class 切换或内联 style 控制 transform,transition 仅作为视觉层面的配合。

Less 里变量无法实时驱动圆形进度条动画
问题很直接:Less 是编译时预处理器,所有 @progress 变量在构建阶段就固化成静态 CSS。你改一个变量值、刷新页面,不会“动起来”——它只是重新生成一套新样式。想让环形进度条随 JS 数据变化而旋转填充,必须靠 class 切换或内联 style 控制 transform,transition 仅作视觉配合。
用 transform: rotate() + clip 实现半圆遮罩逻辑
核心思路其实很巧妙:画一个完整圆环 → 用 clip 或 overflow: hidden 截掉一半 → 再旋转前景层暴露对应角度。Less 可以封装计算逻辑,但最终生效的是编译后的 CSS 值。
clip: rect(0, @size, @size, @size/2)裁出右半圆区域(注意单位统一,@size必须带 px)- 前景层用
border构建环,只设单侧颜色(如border-top-color: transparent),再配transform: rotate(@deg) - 角度与百分比换算:65% →
@deg: (65 * 3.6) - 90(-90 是起始偏移,使 0% 对应 12 点钟方向) - 别直接写
rotate(@p * 3.6),括号必须加:rotate((@p * 3.6) - 90deg),否则单位解析失败
Less mixin 封装要避开作用域陷阱
开发过程中常见的报错是 Variable @p is undefined,多因变量定义在嵌套块或导入文件中。mixin 必须能访问到传入参数,且不依赖外部作用域。
- 正确声明:
.circle-progress(@p) when (@p >= 0) and (@p =< 100) {
.foreground {
transform: rotate((@p * 3.6) - 90deg);
}
.progress-text::after {
content: "@{p}%";
}
} - 调用时直接传整数:
.circle-progress(72); - 避免在
&:hover或:extend()里定义变量,它们会切断 mixin 参数传递链 - 如果后端返回小数(如
0.72),先乘 100:.circle-progress(round(@backend * 100));
响应式适配和动画性能关键点
环形进度条在不同屏幕下缩放容易导致 clip 区域错位、border 渲染模糊。纯百分比尺寸不可靠,需结合视口单位与固定基准。
需要注意的是:
- 容器宽高建议用
vw或rem,但clip的rect()不支持百分比,必须转为像素 —— 所以得用 JS 动态注入或媒体查询多档适配 - 动画用
transform和opacity,别触发布局(width、height)或绘制(background-color) - 真机上
clip兼容性差,iOS Safari 旧版可能失效,备用方案是双层mask+radial-gradient遮罩 - 如果用
transition,务必加will-change: transform提前升层,避免卡顿
真正难的不是怎么写 Less,而是当 JS 更新进度时,如何把数值精准映射到旋转角度、裁剪区域和文本内容三者同步,且不触发重排。这些细节在编译后就固化了,没法靠变量“热更新”。
