先记住这个答案
conic-gradient 是沿着圆心角方向铺颜色的渐变。把两个色标写在同一个角度,比如 green 0deg 120deg, #eee 120deg 360deg,就能画出一块绿色扇形和一块灰色扇形,中间是硬边。给容器设 border-radius: 50% 裁成圆形,再用伪元素或 mask 把中心挖空,就得到了圆环进度条。进度值通过 CSS 自定义属性控制,JavaScript 只需要改一个变量。
conic-gradient沿圆心角方向做颜色插值- 色标同角度产生硬边,天然适合画扇形和饼图
- 圆角裁圆,伪元素或
mask可挖空 - 自定义属性配合
@property可驱动动画
最简单的圆环写法
先做一个正方形容器,用 conic-gradient 画扇形,再用伪元素盖一个白色小圆在中心。这是最容易理解的方案,不依赖 mask。
.ring {
width: 120px;
height: 120px;
border-radius: 50%;
/* --p 是进度角度,由 JS 设置 */
background: conic-gradient(#4caf50 0deg var(--p, 90deg), #eee var(--p, 90deg) 360deg);
position: relative;
}
.ring::after {
content: '';
position: absolute;
inset: 15px; /* 控制圆环粗细 */
border-radius: 50%;
background: #fff; /* 和页面背景同色 */
}inset: 15px 决定了圆环的粗细。伪元素的背景色必须和页面背景一致,否则看起来不像镂空。如果背景不是纯色,就得换成 mask 方案。
用 mask 做真正的透明镂空
当页面背景是图片或渐变时,伪元素盖白色就穿帮了。这时用 radial-gradient 做 mask,中心透明、外圈不透明,就能真正镂空。
mask 的写法是 mask: radial-gradient(closest-side, transparent calc(100% - 15px), #000 calc(100% - 15px))。15px 就是圆环宽度。这种方案不需要伪元素,也不依赖背景色,更通用。
不过 Safari 还需要加 -webkit-mask 前缀。如果项目不需要兼容旧版 Safari,直接用无前缀写法就行。
让进度条动起来
conic-gradient 本身不是可插值类型,直接写 transition: background 0.3s 不会有过渡效果。解决办法是把角度值放到 CSS 自定义属性里,然后用 @property 告诉浏览器它是 <angle> 类型。
注册之后,改变 --p 的值就能触发平滑过渡。JavaScript 里只要 element.style.setProperty('--p', '216deg') 就行,CSS 会自动补间。
容易答错的地方
- 忘了容器必须是正方形
border-radius: 50%只有在宽高相等时才能裁出正圆。如果容器是长方形,裁出来是椭圆,进度条就变形了。实际项目里用aspect-ratio: 1约束一下最稳妥。- 伪元素镂空在非纯色背景上穿帮
- 伪元素方案本质是「盖一个同色圆」,不是真的透明。换了背景就露馅。面试时直接说清楚这个局限性,再提
mask方案,比只说一种写法要好。
面试官还会怎么问?
怎么在圆环上加一个圆头端点?
在进度条末端叠一个小圆点。小圆点用绝对定位,通过 transform: rotate(var(--p)) translateY(-半径) 把它放到圆环的边缘上。注意 rotate 要写在 translateY 前面,否则位移方向会错。
不用 conic-gradient 还能怎么做圆环进度条?
用两个半圆 clip 拼接的老方案:左右各一个半圆,通过 rotate 控制露出的角度。写法比 conic-gradient 复杂得多,但兼容性覆盖到 IE。SVG 的 stroke-dasharray 加 stroke-dashoffset 也是常见方案,语义更好,动画也更方便。
conic-gradient 能画饼图吗?
能。多段硬边色标就是多个扇形,每段的角度占比就是数据占比。比如三段数据分别占 40%、35%、25%,色标写 red 0deg 144deg, blue 144deg 270deg, green 270deg 360deg。再加 border-radius: 50% 就是饼图。不过数据动态变化时建议用 SVG 或 Chart 库,纯 CSS 拼接容易出错。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。