环形数据可视化在后台面板和移动端卡片里很常见,过去我们习惯用SVG描边或者引一个图表库。其实只靠CSS自定义属性和原生渐变,就能让环形进度随数据自动变化,核心是把百分比转成角度交给conic-gradient处理。

一、环形图的基本实现思路
环形图本质上是一个圆被按百分比切分,中间挖空形成环。CSS里最适合做整圆切分的就是conic-gradient,它从圆心出发按角度铺设颜色。如果我们用CSS变量保存数据百分比,就能在渐变中直接引用,避免每次改数据都去手写度数。
具体做法是给容器设好宽高和圆角,背景写成conic-gradient,颜色断点用var读取变量。中间孔洞用伪元素盖一个同色小圆即可。这种方式不需要任何脚本参与渲染,数据变了只需改自定义属性,浏览器自动重绘。
<div class="ring" style="--val:65;"></div>
.ring {
width: 200px;
height: 200px;
border-radius: 50%;
background: conic-gradient(#3b82f6 calc(var(--val) * 1%), #e5e7eb 0);
position: relative;
}
.ring::after {
content: "";
position: absolute;
inset: 20px;
background: #fff;
border-radius: 50%;
}
二、用CSS变量动态计算角度
上面的例子已经用了calc把--val直接当百分比,但有时设计稿给的是0到1的小数,或者要求角度从顶部开始。conic-gradient默认从12点钟方向顺时针起算,如果我们存的是0到100的整数,直接乘1%最直观;若是小数,就改成calc(var(--val) * 100%)。
更严谨的写法是用@property注册变量类型,这样浏览器知道它是数字,能做平滑过渡。没有注册时,var在渐变里只是文本替换,切换数值不会触发动画。下面演示带注册的写法,并让环形在加载后从0走到目标值。
@property --val {
syntax: "<number>";
inherits: false;
initial-value: 0;
}
.ring {
--val: 65;
background: conic-gradient(#3b82f6 calc(var(--val) * 1%), #e5e7eb 0);
transition: --val 1s ease;
}
在JS里只需写document.querySelector('.ring').style.setProperty('--val', 80),环形就会动画到80%。如果完全不用JS,也可以在HTML行内style写死--val,依旧是纯静态环图。
三、多环与文字标注的叠加
实际项目常要在一张图里叠多个环,比如完成率加对比率。思路是放几个绝对定位的.ring,各自用不同--val和颜色,再用nth-child调小尺寸做出同心环。中心放一个绝对定位的span显示主指标文字。
要注意伪元素遮孔的尺寸得随环大小比例走,否则孔偏了就不好看。文字用transform居中即可。下表列出常见参数关系:
| 变量名 | 含义 | 示例值 |
|---|---|---|
| --val | 数据百分比数值 | 65 |
| inset | 中心孔偏移 | 20px |
| transition | 角度变化动画 | 1s ease |
<div class="ring" style="--val:65;"> <span>65%</span> </div>
.ring span {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
z-index: 1;
}
四、优点与要注意的坑
纯CSS环图体积小、无依赖,适合轻量卡片。conic-gradient现代浏览器都支持,配合@property能在支持的引擎里做丝滑过渡。相比SVG,它写起来更贴近布局思维,改主题色只用换渐变停点。
坑主要有两个:一是老版本浏览器不识@property,变量切换会瞬变而非动画;二是conic-gradient不能直接做圆角端点,若想让环头圆润,还得叠mask或改用SVG。若数据来自接口,用一小段JS设变量即可,不必整库引入。
总结:把数据塞进CSS变量,用calc映射成conic-gradient的角度,是纯前端环形可视化的捷径。掌握@property注册与伪元素遮孔,就能覆盖多数仪表盘需求。
CSS变量环形图表conic-gradient修改时间:2026-08-03 23:24:27