CSS 实现渐变圆弧进度条

最近一段时间,业务上需要开发一个仪表盘页面,其中有个部分是圆弧进度条的效果,如下图所示:

其具体需求如下:

  • 半圆形进度条,中间是进度数字,圆弧渐变背景表示进度,从 0% 到 100%
  • 20%进度以下绿色渐变,20%~80% 蓝色渐变,80% 以上橙色渐变

首先写一个函数,根据进度设置渐变色值:

js 复制代码
// 根据比例范围,设置渐变弧形进度条的两端色值
function getColor(percent: number) {
  if (percent >= 80) return ['#FC8D2A', '#FC5618']
  if (percent >= 20) return ['#179AF5', '#0F51FD']
  return ['#24D32D', '#1CAA0D']
}

然后开始做半圆形进度条组件,以 React 为例,组件实现如下(Vue 的实现是类似的):

js 复制代码
function CircleProgress({ percent }) {
  const [start, end] = getColor(percent)
  const deg = 1.8 * percent
  const background = `conic-gradient(from -90deg at bottom,  ${start} 0deg,  ${end} ${deg}deg, #e8ebee ${deg}deg)`
  return (
    <div className="circle-progress-container" style={{ background }}>
      <div className="circle-progress-inner">
        <div className="status-number">{percent}%</div>
      </div>
    </div>
  )
}

CSS 代码如下:

css 复制代码
.circle-progress-container {
  width: 100px;
  height: 50px;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  position: relative;
  transition-property: background;
  transition-duration: 300ms;
  transition-timing-function: ease;
}
.circle-progress-inner {
  width: 80px;
  height: 40px;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  background-color: #eff3fc;
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
}
.status-number {
  margin-top: 5px;
  font-weight: bold;
}

代码看起来非常简单,现对其实现进行拆解:

用 CSS 画半圆

用 CSS 如何画圆弧呢?有两种实现方式:

  • 画一个半圆,然后设置 border
  • 画两个半圆,然后设置 background

可以看到,这两种方式都要求先画半圆。那如何用 CSS 画半圆呢?首先在 HTML 里面定义一个类名为 container 的容器:

html 复制代码
<div class="container"></div>

然后对其设置 CSS 样式:

css 复制代码
.container {
  width: 500px;
  height: 250px;
  box-sizing: border-box;
  background-color: tomato;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
}

这样就能得到一个完美的半圆形了:

注意,这里面有两个关键点:

  • 容器是一个矩形区域,且宽度是高度的两倍
  • 左上角和右上角的弧形半径保持跟宽度一致

用 CSS 画圆弧

掌握了如何用 CSS 画半圆之后,接下来就开始画圆弧了,对上面说的两种方案都做了尝试:

方案一:为单个半圆设置 border

只要设置容器的 border,并且把 border-bottom 设置为空即可:

css 复制代码
.container {
  width: 500px;
  height: 250px;
  box-sizing: border-box;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  border: 25px solid tomato;
  border-bottom: none;
}

注意这里的容器是矩形区域,而不是半圆,你可能会这样设置(我就犯了类似的错误):

css 复制代码
.container {
  width: 500px;
  height: 250px;
  box-sizing: border-box;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  border-top: 25px solid tomato;
}

这是不对的,因为会得到下面的图案:

但是当需要设置渐变色的时候,卡住了,CSS 并没有提供一个能够为 border 设置渐变色的 API,唯一可以在 border 上做定制的是 border-image 属性,例如:

css 复制代码
.container {
  width: 500px;
  height: 250px;
  box-sizing: border-box;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  border: 25px solid tomato;
  border-bottom: none;
  border-image-source: linear-gradient(to right, tomato, blue);
  border-image-slice: 1;
}

然而非常遗憾的是,border-image 无法配合 border-radius 一起使用,最终得到的效果如下:

虽然渐变有了但圆角没了,也就是说,无法在半圆的边框上设置渐变色,于是这种方案只能被弃用了。

方案二:为两个半圆分别设置 background

首先需要嵌套的 DOM 结构,设置外层和内层两个半圆::

html 复制代码
<!-- 外层半圆 -->
<div className="circle-progress-container">
<!-- 内层半圆 -->
  <div className="circle-progress-inner"></div>
</div>

外层半圆的样式:

css 复制代码
.circle-progress-container {
  width: 100px;
  height: 50px;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  position: relative;
  background: tomato; /* 外层半圆背景色 */
}

内层半圆的样式:

css 复制代码
.circle-progress-inner {
  width: 80px;
  height: 40px;
  border-top-left-radius: 50% 100%;
  border-top-right-radius: 50% 100%;
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  background: white; /* 内层半圆背景色 */
}

注意这里:

为了确保外层半圆和内层半圆的圆心重叠,需要设置外层半圆的定位为相对定位,内层为绝对定位并居中。

接下来的关键就是如何设置渐变色了,由于圆环的颜色是外层半圆的背景色,CSS 提供了一个 cornic-gradient 属性,可以用于设置环形渐变,例如下面的代码:

css 复制代码
background: conic-gradient(red, orange, yellow, green, blue);

得到的效果就是以圆心为中心,向四周扩散的渐变效果,大家可以在 MDN 文档中进行体验:

注意,cornic-gradient 的效果和 radical-gradient 是不一样的,容易搞混淆。前者是从圆心向任意方向射出的直线上的颜色都一样,而后者是跟圆心距离相同的圆环上的颜色都一样:

因此,只需要拿到渐变两端的颜色,对 CSS 值进行拼接即可:

js 复制代码
const [start, end] = getColor(percent)
const deg = 1.8 * percent
const background = `conic-gradient(from -90deg at bottom,  ${start} 0deg,  ${end} ${deg}deg, #e8ebee ${deg}deg)`

这里把内层半圆去掉,可以更清楚的看到 cornic-gradient 实现的渐变效果:

内层半圆添加上背景色,就完美实现了开头的弧形渐变。

总结

阅读本文,可以掌握以下知识点:

  • 如何用 CSS 画一个半圆
  • 如何用 CSS 画一个纯色圆弧
  • 如何用 cornic-gradient 画渐变背景
  • 如何用 CSS 画一个渐变色圆弧
相关推荐
qq_364371726 分钟前
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
前端·vue.js·缓存
y先森1 小时前
CSS3中的弹性布局之侧轴的对齐方式
前端·css·css3
y先森6 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy6 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189116 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿7 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡8 小时前
commitlint校验git提交信息
前端
虾球xz9 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇9 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒9 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript