如何用CSS绘制饼图

2020-06-19 / 196

前言

数据大屏还蛮常见这种的。反正之前是直接切图。今日早读文章由360@月影授权分享。

正文从这开始~~

饼图是数据统计图表里常用的图形,在很多应用中都需要使用。

对于简单的饼图,不需要引入复杂的图表库,只要用简单的代码就能完成。

一般的两色饼图大概是如下这个样子:

 

关于上图的这个效果,我们可以通过比较复杂的HTML结构加上一些JS来完成这个工作。但是如果考虑到代码可维护性,像这样纯UI的工作最好交由CSS完成,让HTML元素尽量保持简洁,比如,保持一个简单的HTML标签:

<divclass="pie">25%</div
 

那么,如何使用CSS实现饼图的效果呢?比较直接的思路是采用颜色拼接和旋转来实现。

首先,我们先实现一个单色饼图。这个效果非常容易实现,可以使用CSS的border-radius属性将元素的边框四角分别设置为1/4圆弧,如下代码所示:

<divclass="pie"></div
.pie { display: inline-block; width: 150px; height: 150px; border-radius: 50%; background: #3c7; }

 

有了单色饼图,那么双色饼图该怎么做呢?还记得上一个故事里我们使用border来做三角形和其他有趣图案吗?结合border-radius,我们可以将三角形变成扇形!

.pie {
  display: inline-block;
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background: #3c7;
  border: solid 10px;
  border-color: red blue orange green;
  box-sizing: border-box;
}

上面的代码中,我们先给.pie元素的上、右、下、左边框设置不同颜色,以及边框大小10px。它的效果如下图所示:

然后,我们将元素的width、height属性都修改为0,并将border的大小设置为75px:

.pie {
  display: inline-block;
  width: 0;
  height: 0;
  border-radius: 50%;
  border: solid 75px;
  border-color: red blue orange green;
  box-sizing: border-box;
}

这时.pie元素的效果就是下面这个样子——四个扇形:

然后,再将上边框和右边框的颜色设置为绿色(#3c7),下边框和左边框的颜色设置为蓝色(#37c),如下代码所示:

.pie {
  display: inline-block;
  width: 0;
  border-radius: 50%;
  border: solid 75px;
  border-color: #3c7 #3c7 #37c #37c;
  box-sizing: border-box;
}

这样,.pie元素就变成下面的样子:

再通过transform属性将这个元素旋转45度:

.pie {
  display: inline-block;
  width: 0;
  border-radius: 50%;
  border: solid: 75px;
  border-color: #3c7 #3c7 #37c #37c ;
  box-sizing: border-box;
  transform: rotate(45deg);
}

这就得到我们要的两色饼图:

现在,我们需要思考的是:如何让两种颜色,按照给定的进度数据,显示对应的比例呢?

 

它的基本思路是这样的:

 

在这个元素上叠加一层饼图;

初始状态下,我们将这张饼图的右半边(即,上、右边框)的颜色设置为蓝色;左半边(即,下、左边框)的颜色设置为透明色。这样初始情况下,.pie元素右半边绿色的部分,被这一叠加层覆盖为蓝色,从视觉上看.pie元素此时的进度是0;

根据需求,以不同角度旋转这个叠加层,这样就实现了不同百分比的饼图。

首先,叠加的饼图可以用伪元素来实现:

.pie,.pie::before {
  display: inline-block;
  width: 0;
  border-radius: 50%;
}

.pie {
  position: relative;
  border: solid 75px;
  border-color: #3c7 #3c7 #37c #37c;
  box-sizing: border-box;
  transform: rotate(45deg);
}

.pie::before {
  content: '';
  position: absolute;
  border: solid 75px;
  border-color: #37c #37c transparent transparent;
  transform: translate(-50%, -50%);
}

如上代码所示,我们将.pie::before伪元素盖在.pie元素上,这个伪元素的border一半是透明色,一半是蓝色,恰好是.pie元素左边的颜色,那么这样饼图看起来又恰好是一个整圆了。

在.pie::before规则中,还需要注意的是这个声明——transform: translate(-50%, -50%)。因为元素的定位在元素边框(border-box)的左上角,而相对位置(0,0)则在元素内容区(content-box)的左上角。因为.pie元素的width和height都是0,所以content-box的左上角正好是.pie的中心点。因此要给伪元素设置一个向左向上各偏移50%的位置,才能恰好覆盖住.pie元素。

这时,这个元素的效果如下图所示:

然后,我们把伪元素旋转一个角度,比如转过10%,只要在伪元素的tranform属性上增加一个rotate,并让它的值等于0.1turn,如下代码所示:

/*省略其他的CSS规则...*/

.pie::before {
  content: '';
  position: absolute;
  border: solid 75px;
  border-color: #37c #37c transparent transparent;
  transform: translate(-50%, -50%) rotate(0.1turn);
}

在这里,为了看清楚,我把before伪元素的一半border-color暂时换成了红色,显示结果如下:

好,我们把颜色换回来,这样就得到我们期望的结果:

所以,只要给这个伪元素设置不同的旋转角度,就能得到相应的统计饼图,如下图所示(为了看起来更清晰,我们把蓝色部分的伪元素颜色替换为了半透明的):

问题解决到这里,我们只解决了前半圈,也就是只能展示从0%到50%的进度表示,而超过50%的进度就行不通了。如下代码所示:

.pie::before {
  content: '';
  position: absolute;
  border: solid 75px;
  border-color: #37c #37c transparent transparent;
  transform: translate(-50%, -50%) rotate(0.6turn);
}

上面的代码中, 我们将伪元素旋转到0.6trun(也就是旋转了60%),这时饼图变成下面这个样子

这是因为我们的.pie元素只有右边一半是绿色,所以旋转超过了50%后,左边的蓝色区域就裸露了出来,这显然不是我们期望的结果。

 

要解决这个问题,我们需要调整伪元素的左右两半圆的颜色配置。初始情况下,我们将这个伪元素的下、左边框设置为透明,上、右边框设置为蓝色,即:左半圆是透明色,右半圆是蓝色。当旋转到50%时,伪元素的蓝色右半圆被旋转到.pie元素的左边,而透明的左半圆被旋转到.pie元素的右边。这时,我们只需要将原来是透明色的下、左边框设置为绿色,将原来是蓝色的上、右边框设置为透明色,就能解决超过上面的问题。

<divclass="pie convex"></div
.pie.convex::before {
  border-color: transparent transparent #3c7 #3c7;
}

如上代码所示,我们给超过50%进度的饼图元素加一个类选择器convex。在这个选择器里,我们将伪元素的上、右边框颜色设置为透明色,将下、左边框的颜色设置为绿色。

这样,超过50%进度的饼图就正常显示了:

到这里,基本上,我们可以使用CSS实现静态的饼图效果。比如,要实现最初的四张饼图,它们的代码如下所示:

在线演示

<divclass="pie one"></div
<divclass="pie two"></div
<divclass="pie three"></div
<divclass="pie convex four"></div
.pie,.pie::before {
  display: inline-block;
  width: 0;
  border-radius: 50%;
}

.pie {
  position: relative;
  border: solid 75px;
  border-color: #3c7 #3c7 #37c #37c;
  box-sizing: border-box;
  transform: rotate(45deg);
}

.pie::before {
  content: '';
  position: absolute;
  border: solid 75px;
  border-color: #37c #37c transparent transparent;
  transform: translate(-50%, -50%);
}

.pie.convex::before {
  border-color: transparent transparent #3c7 #3c7;
}

.pie.one::before {
  transform: translate(-50%, -50%) rotate(.1turn);
}

.pie.two::before {
  transform: translate(-50%, -50%) rotate(.25turn);
}

.pie.three::before {
  transform: translate(-50%, -50%) rotate(.5turn);
}

.pie.four::before {
  transform: translate(-50%, -50%) rotate(.8turn);
}

这样,我们想要的饼图效果就完成了。

 

这个饼图效果虽然用纯CSS实现了,但是它还有几个问题:

 

通常情况下,我们的统计数据都是来自服务器,是动态而不是静态的,所以饼图的进度比例是需要动态绑定而不是写死在CSS中。(伪元素并不好用JS操作,也不能被inline-css控制)

 

饼图上的文字我们并未加上,如何在不改变HTML结构的情况下加上居中显示的进度文字呢?

 

我们只能显示双色饼图,如果要显示更多颜色,甚至任意多颜色的饼图,该如何做呢?

 

关于本文 作者:@月影 原文:https://mp.weixin.qq.com/s/_W2-iRX0poEgNq7sZdn53g

 

下一篇:CSS 盒子模型