直接用 width 百分比加 css 变量会失效,因为 width 不接受 css 变量作为数值单位(如 --progress: 65 不能直接写成 width: var(--progress)%),浏览器将 65% 解析为非法值而回退为 auto 或 0。

为什么直接用 width 百分比 + CSS 变量会失效?
因为 width 不接受 CSS 变量作为数值单位(比如 --progress: 65 不能直接写成 width: var(--progress)%),浏览器会把 65% 解析成非法值,最终回退为 auto 或 0。
真正能响应变量变化的方案是用 transform: scaleX() 或 clip-path,它们支持 calc() 和变量组合。
- 推荐用
transform: scaleX(calc(var(--progress) / 100)):简单、性能好、兼容性佳(Chrome 36+、Firefox 16+、Safari 9+) - 避免用
width: calc(var(--progress) * 1px):需要额外设定容器max-width,且单位换算易出错 - 如果要圆角过渡或遮罩效果,改用
clip-path: inset(0 0 0 calc(100% - var(--progress) * 1%))
scaleX() 动态填充的实际写法
假设你有一个进度条容器 .progress-bar,内部一个 .fill 元素用于填充:
:root {
--progress: 72;
}
<p>.progress-bar {
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}</p><p>.fill {
height: 100%;
background: #4a9eff;
transform-origin: left;
transform: scaleX(calc(var(--progress) / 100));
transition: transform 0.3s ease;
}</p>
关键点:
-
transform-origin: left确保缩放从左端开始,否则会居中收缩 - 必须设
overflow: hidden在父容器上,否则缩放超出会溢出 - 动画生效需加
transition,但只作用于transform,不要写all - JS 更新时只需修改
document.documentElement.style.setProperty('--progress', value)
如何用 JS 安全更新变量并避免抖动?
直接高频调用 setProperty 可能触发重排或卡顿,尤其在动画循环中。
- 用
requestAnimationFrame批量更新:确保每帧最多一次变量写入 - 对输入值做边界限制:
Math.min(100, Math.max(0, value)),防止负数或超 100 导致scaleX异常 - 不要在
for循环里连续 setProperty —— 合并成一次,或用setTimeout(..., 0)节流 - 若进度来自 API,建议加 loading 状态控制,避免未初始化时
--progress为 undefined 导致计算失败
仪表盘里多个进度条共用变量时要注意什么?
全局变量(:root)不适合多实例,否则所有进度条同步变化。必须用局部作用域。
- 给每个进度条容器加唯一 class 或 data 属性,例如
.progress-bar[data-id="cpu"] - 用 JS 为每个容器单独设置
style.setProperty,而不是操作:root - 或者改用自定义属性在元素级声明:
<div class="progress-bar" style="--progress: 45">,CSS 中用 <code>.progress-bar .fill { transform: scaleX(calc(var(--progress) / 100)); } - 注意:伪元素(如
::before)无法读取父元素的 inline style 变量,必须显式继承或用inherit传递
变量名冲突和作用域混淆,是仪表盘批量渲染时最常被跳过的细节。











