如何在CSS中制作饼图

38
如何使用CSS创建像下面这样的饼图?

enter image description here


5
了解:您的“圆圈图表”被称为饼图 :o) - balexandre
  1. http://www.htmldrive.net/items/show/751/Pure-CSS3-Pie-Charts-effect
  2. http://atomicnoggin.ca/blog/2010/02/20/pure-css3-pie-charts/
  3. http://elisabethrobson.com/?p=867 (使用HTML5画布)
  4. 你也可以使用HTML5和jQuery,链接
- Sirwan Afifi
10个回答

34

I saw some people opting for Google Developers Tool, its very tough and it also uses JS and you only want CSS. So here is the most easy way, Pure CSS, made by using background gradient.

.pie {
  width: 400px;
  height: 400px;
  background-image: conic-gradient(orange 64%, blue 64%, blue 81%, black 81%);
  border-radius: 50%
}
<div class="pie"></div>


5
Firefox(69)报告 background-image 无效,不显示任何内容。 - Martin Tournoij
厉害的技巧!对于不支持的浏览器,我们可以使用 polyfill https://github.com/LeaVerou/conic-gradient。 - nektobit
更新:现在这也可以在 Firefox (99.0) 上工作了。 - Netsu
另请参考 Mozilla 的示例:https://developer.mozilla.org/zh-CN/docs/Web/CSS/gradient/conic-gradient#gradient_pie-chart - kca
1
这会导致蓝色和黑色之间的渐变。为了确保硬边界,在边界点上加倍颜色,并将百分比设置为起始和结束。 (橙色:0%,橙色:64%,蓝色64%,蓝色:81%,黑色:81%,黑色:100%)(第一个和最后一个(0%/100%)是推断出来的,所以可以省略)。 - Plagiatus
显示剩余2条评论

28

我发现这个是最简单的仅使用CSS的解决方案。以下是略微简化后的内容。

    .pieContainer {
      height: 150px;
      position: relative;
    }
    
    .pieBackground {
      position: absolute;
      width: 150px;
      height: 150px;
      border-radius: 100%;
      box-shadow: 0px 0px 8px rgba(0,0,0,0.5);
    } 
    
    .pie {
      transition: all 1s;
      position: absolute;
      width: 150px;
      height: 150px;
      border-radius: 100%;
      clip: rect(0px, 75px, 150px, 0px);
    }
    
    .hold {
      position: absolute;
      width: 150px;
      height: 150px;
      border-radius: 100%;
      clip: rect(0px, 150px, 150px, 75px);
    }
    
    #pieSlice1 .pie {
      background-color: #1b458b;
      transform:rotate(30deg);
    }
    
    #pieSlice2 {
      transform: rotate(30deg);
    }
    
    #pieSlice2 .pie {
      background-color: #0a0;
      transform: rotate(60deg);
    }
    
    #pieSlice3 {
      transform: rotate(90deg);
    }
    
    #pieSlice3 .pie {
      background-color: #f80;
      transform: rotate(120deg);
    }
    
    #pieSlice4 {
      transform: rotate(210deg);
    }
    
    #pieSlice4 .pie {
      background-color: #08f;
      transform: rotate(10deg);
    }
    
    #pieSlice5 {
      transform: rotate(220deg);
    }
    
    #pieSlice5 .pie {
      background-color: #a04;
      transform: rotate(70deg);
    }
    
    #pieSlice6 {
      transform: rotate(290deg);
    }
    
    #pieSlice6 .pie {
      background-color: #ffd700;
      transform: rotate(70deg);
    }
    
    .innerCircle {
      position: absolute;
      width: 120px;
      height: 120px;
      background-color: #444;
      border-radius: 100%;
      top: 15px;
      left: 15px; 
      box-shadow: 0px 0px 8px rgba(0,0,0,0.5) inset;
      color: white;
    }
    .innerCircle .content {
      position: absolute;
      display: block;
      width: 120px;
      top: 30px;
      left: 0;
      text-align: center;
      font-size: 14px;
    }
    <div class="pieContainer">
      <div class="pieBackground"></div>
      <div id="pieSlice1" class="hold"><div class="pie"></div></div>
      <div id="pieSlice2" class="hold"><div class="pie"></div></div>
      <div id="pieSlice3" class="hold"><div class="pie"></div></div>
      <div id="pieSlice4" class="hold"><div class="pie"></div></div>
      <div id="pieSlice5" class="hold"><div class="pie"></div></div>
      <div id="pieSlice6" class="hold"><div class="pie"></div></div>
      <div class="innerCircle"><div class="content"><b>Data</b><br>from 16<sup>th</sup> April, 2014</div></div>
    </div>


3
正确。对于超过50%的切片,您需要创建两个切片:一个占50%,另一个占剩余部分。 - MastaBaba
1
这是一个很好的答案,除了这里显示的饼图与原始问题中显示的饼图相当不同。 - SendETHToThisAddress

10

我相信您可以从Google可视化API参考页面下载google.visualization API,但是服务条款不允许您下载、保存或托管google.load或google.visualization代码。 - ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
6
这并不是你要求的在CSS中制作图表的答案,因此不应该被接受。如果你喜欢这个答案,你应该把问题改为“如何在JavaScript/CSS中制作饼图”。 - hiburn8
2
你的回答中有一个失效的链接,这里提供另一个可以加入列表的链接:https://gcemetery.co/。 - Dreaded Singleton
既然链接已经失效,这个回答就不再有效,应该取消采纳(即使有链接,也并不意味着它是好的)。 - InSync
现在链接已经失效,这不再是一个答案,应该被取消采纳(即使有链接,它也绝不算好)。 - InSync

2

我尝试了一些答案,但对于动态数据集来说,它们似乎过于复杂和难以采用。

我不需要在图表中打印数据,所以最终我选择了最简单的解决方案:

<div style="
        border-radius: 100%;
        width: 150px;
        aspect-ratio: 1;
        background: conic-gradient(blue 0deg 172deg, red 172deg 249deg, yellow 249deg 313deg, green 313deg 333deg, lightgreen 333deg 347deg, gold 347deg 360deg, pink 360deg 360deg
            );
    "></div>

它不需要CSS类,可以嵌入任何HTML中,并且conic-gradient内的字符串可以轻松地从值列表构造。

希望它作为构建饼图的简单方法会很有用。


2
通常使用纯CSS创建图表并不是最好的方法,最好使用canvas或外部库。
这里有一个不使用外部库的饼图,使用HTML5 canvas(fiddle):

enter image description here

var canvas = document.getElementById("can");
var ctx = canvas.getContext("2d");
var lastend = 0;
var data = [60,210,90];
var myTotal = 0;
var myColor = ['#afcc4c', '#95b524','#c1dd54'];
var labels = ['B', 'A', 'C'];

for(var e = 0; e < data.length; e++)
{
  myTotal += data[e];
}

// make the chart 10 px smaller to fit on canvas
var off = 10
var w = (canvas.width - off) / 2
var h = (canvas.height - off) / 2
for (var i = 0; i < data.length; i++) {
  ctx.fillStyle = myColor[i];
  ctx.strokeStyle ='white';
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.moveTo(w,h);
  var len =  (data[i]/myTotal) * 2 * Math.PI
  var r = h - off / 2
  ctx.arc(w , h, r, lastend,lastend + len,false);
  ctx.lineTo(w,h);
  ctx.fill();
  ctx.stroke();
  ctx.fillStyle ='white';
  ctx.font = "20px Arial";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  var mid = lastend + len / 2
  ctx.fillText(labels[i],w + Math.cos(mid) * (r/2) , h + Math.sin(mid) * (r/2));
  lastend += Math.PI*2*(data[i]/myTotal);
}
html, body{
  background: #c4c9e9
}
  <canvas id="can" width="200" height="200" />

Fiddle(代码基于this的解决方案编写)

但最好使用绘制图表的库。 在apex-charts中有一个称为sparkline的选项,它可以帮助您删除多余的内容并绘制出简洁明了的图表。

这是一个使用apex-charts库创建的干净的环形图。 (使用sparkline选项删除了额外的内容):

enter image description here

var options = {
  series: [620, 40],
  labels: ['Finished', 'Unfinished'],
  chart: {
    type: 'donut',
    sparkline: {
      enabled: true,
    }
  },
  plotOptions: {
    pie: {
      donut: {
        labels: {
          show: true,
          total: {
            showAlways: false,
            show: true,
            label: 'Total'
          }
        }
      }
    }
  },
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();

在CodePen上查看它


1
有趣的JS解决方案;不幸的是,这个问题正在问如何在CSS中实现,所以这个答案在这里没有用处。 - TylerH
@TylerH 更新了答案,并解释了为什么这是一个 JavaScript 解决方案,而不是 CSS。 - yaya

2

1
使用仅限于HTML和CSS的最简单方法是使用以下代码:

.graph {
  display: flex;
  position: relative;
  justify-content: center;
  align-items: center;
  margin: 50px 0;
  width: 150px;
  text-align: center;
}

.pie {
  width: 150px;
  aspect-ratio:1;
  position:absolute;
}
.pie:before,
.pie:after {
  content:"";
  position:absolute;
  border-radius:50%;
}
.pie:before {
  inset:0;
  background: radial-gradient(farthest-side,var(--c) 98%,#0000) top/var(--b) var(--b) no-repeat,
          conic-gradient(var(--c) calc(var(--p)*1%),#0000 0);
}
.no-round:before {
  background-size:0 0,auto;
}
.no-round:after {
  content:none;
}
<div style="display: flex; justify-content: center">
  <div style="display: flex; flex-flow: wrap; justify-content: space-around; max-width: 400px; width: 100%">
    <div class="graph" title="some title to display on hover">
      <div class="pie no-round" style="--p:83;--c:#FC4819;--b:15px"></div>
      <div class="pie no-round" style="--p:11;--c:#0089B0;--b:15px; rotate: 0.83turn"></div>
      <div class="pie no-round" style="--p:6;--c:#23B032;--b:15px;rotate: 0.94turn"></div>
    </div>
  </div>                        
</div>

您可以随便添加任意数量的扇形,只需在 div class='graph' 中添加一个新的 div class='pie no-round' 即可。
在 div pie 块中的 --p 属性将设置其大小,而 rotate 将设置其起始点。例如,如果您想要一个图表,其中包含两个扇形,一个占 60%,另一个占 40%,第一个将具有 --p:60,第二个将具有 --p:40;rotate:0.60turn
如果您想要 3 个扇形(50%、40%、10%),则必须将前两个值相加以设置正确的旋转属性用于第三个值,结果如下所示:
第一个饼图:--p:50 第二个饼图:--p:40;rotate:0.50turn 第三个饼图:--p:10;rotate:0.90turn # 0.90 being 50 + 40 您可以从那里进行自定义,可以在每个扇形内部添加标签,如下所示:

.graph {
  display: flex;
  position: relative;
  justify-content: center;
  align-items: center;
  margin: 50px 0;
  width: 150px;
  text-align: center;
}

.label {
  position: absolute;
  color: white;
  padding-top: 5%;
  padding-left: 5%;
  font-size: 0.8rem;
}

.pie {
  width: 150px;
  aspect-ratio:1;
  position:absolute;
}
.pie:before,
.pie:after {
  content:"";
  position:absolute;
  border-radius:50%;
}
.pie:before {
  inset:0;
  background: radial-gradient(farthest-side,var(--c) 98%,#0000) top/var(--b) var(--b) no-repeat,
          conic-gradient(var(--c) calc(var(--p)*1%),#0000 0);
}
.no-round:before {
  background-size:0 0,auto;
}
.no-round:after {
  content:none;
}
<div style="display: flex; justify-content: center">
                      <div style="display: flex; flex-flow: wrap; justify-content: space-around; max-width: 400px; width: 100%">
                        <div class="graph" title="some title to display on hover">
                          <div class="pie no-round" style="--p:83;--c:#FC4819;--b:15px"><strong class="label">83%</strong></div>
                          <div class="pie no-round" style="--p:11;--c:#0089B0;--b:15px; rotate: 0.83turn"><strong class="label" style="rotate: -0.83turn">11%</strong></div>
                          <div class="pie no-round" style="--p:6;--c:#23B032;--b:15px;rotate: 0.94turn"><strong class="label" style="rotate: -0.94turn">6%</strong></div>
                          
                        </div>
                        <div class="graph" title="some title to display on hover">
                          <div class="pie no-round" style="--p:100;--c:#FC4819;--b:15px"></div>
                          <div class="pie no-round" style="--p:0;--c:#23B032;--b:15px; rotate: 0.100turn"></div>
                          <h4 style="max-width: 100px">0% Releases Distributed</h4>
                        </div>
                      </div>
                    </div>

如果您想要添加中心标签,可以这样做:

.graph {
  display: flex;
  position: relative;
  justify-content: center;
  align-items: center;
  margin: 50px 0;
  width: 150px;
  text-align: center;
}

.pie {
  width: 150px;
  aspect-ratio:1;
  position:absolute;
}
.pie:before,
.pie:after {
  content:"";
  position:absolute;
  border-radius:50%;
}
.pie:before {
  inset:0;
  background: radial-gradient(farthest-side,var(--c) 98%,#0000) top/var(--b) var(--b) no-repeat,
          conic-gradient(var(--c) calc(var(--p)*1%),#0000 0);
  -webkit-mask:radial-gradient(farthest-side,#0000 calc(99% - var(--b)),#000 calc(100% - var(--b)));
  mask:radial-gradient(farthest-side,#0000 calc(99% - var(--b)),#000 calc(100% - var(--b)));
}
.no-round:before {
  background-size:0 0,auto;
}
.no-round:after {
  content:none;
}
<div style="display: flex; flex-flow: wrap; justify-content: space-around; max-width: 400px; width: 100%">
                        <div class="graph" title="some title to display on hover">
                          <div class="pie no-round" style="--p:83;--c:#FC4819;--b:15px"></div>
                          <div class="pie no-round" style="--p:11;--c:#0089B0;--b:15px; rotate: 0.83turn"></div>
                          <div class="pie no-round" style="--p:6;--c:#23B032;--b:15px;rotate: 0.94turn"></div>
                          <h4 style="max-width: 100px">6% something</h4>
                        </div>
                        <div class="graph" title="some title to display on hover">
                          <div class="pie no-round" style="--p:100;--c:#FC4819;--b:15px"></div>
                          <div class="pie no-round" style="--p:0;--c:#23B032;--b:15px; rotate: 0.100turn"></div>
                          <h4 style="max-width: 100px">0% something else</h4>
                        </div>
                      </div>


已点赞,但在Chrome 110.0.5481.178中,一些切片边缘不够平滑。我认为这与代码无关;我在其他精确平滑/对接情况下看到过类似的行为。 - Tim M.

0

我在 CodePen 上找到了这个解决方案。你可以更改数据属性中的值,这正是我所需要的:

/* 
  make each pie piece a rectangle twice as high as it is wide.
  move the transform origin to the middle of the left side.
  Also ensure that overflow is set to hidden.
*/
  .pie {
        position:absolute;
        width:100px;
        height:200px;
        overflow:hidden;
        left:150px;
        -moz-transform-origin:left center;
        -ms-transform-origin:left center;
        -o-transform-origin:left center;
        -webkit-transform-origin:left center;
        transform-origin:left center;
    }
/*
  unless the piece represents more than 50% of the whole chart.
  then make it a square, and ensure the transform origin is
  back in the center.

  NOTE: since this is only ever a single piece, you could
  move this to a piece specific rule and remove the extra class
*/
    .pie.big {
        width:200px;
        height:200px;
        left:50px;
        -moz-transform-origin:center center;
        -ms-transform-origin:center center;
        -o-transform-origin:center center;
        -webkit-transform-origin:center center;
        transform-origin:center center;
    }
/*
  this is the actual visible part of the pie. 
  Give it the same dimensions as the regular piece.
  Use border radius make it a half circle.
  move transform origin to the middle of the right side.
  Push it out to the left of the containing box.
*/
    .pie:BEFORE {
        content:"";
        position:absolute;
        width:100px;
        height:200px;
        left:-100px;
        border-radius:100px 0 0 100px;
        -moz-transform-origin:right center;
        -ms-transform-origin:right center;
        -o-transform-origin:right center;
        -webkit-transform-origin:right center;
        transform-origin:right center;
        
    }
 /* if it's part of a big piece, bring it back into the square */
    .pie.big:BEFORE {
        left:0px;
    }
/* 
  big pieces will also need a second semicircle, pointed in the
  opposite direction to hide the first part behind.
*/
    .pie.big:AFTER {
        content:"";
        position:absolute;
        width:100px;
        height:200px;
        left:100px;
        border-radius:0 100px 100px 0;
    }
/*
  add colour to each piece.
*/
    .pie:nth-of-type(1):BEFORE,
    .pie:nth-of-type(1):AFTER {
        background-color:blue;  
    }
    .pie:nth-of-type(2):AFTER,
    .pie:nth-of-type(2):BEFORE {
        background-color:green; 
    }
    .pie:nth-of-type(3):AFTER,
    .pie:nth-of-type(3):BEFORE {
        background-color:red;   
    }
    .pie:nth-of-type(4):AFTER,
    .pie:nth-of-type(4):BEFORE {
        background-color:orange;    
    }
/*
  now rotate each piece based on their cumulative starting
  position
*/
    .pie[data-start="30"] {
        -moz-transform: rotate(30deg); /* Firefox */
        -ms-transform: rotate(30deg); /* IE */
        -webkit-transform: rotate(30deg); /* Safari and Chrome */
        -o-transform: rotate(30deg); /* Opera */
        transform:rotate(30deg);
    }
    .pie[data-start="60"] {
        -moz-transform: rotate(60deg); /* Firefox */
        -ms-transform: rotate(60deg); /* IE */
        -webkit-transform: rotate(60deg); /* Safari and Chrome */
        -o-transform: rotate(60deg); /* Opera */
        transform:rotate(60deg);
    }
    .pie[data-start="100"] {
        -moz-transform: rotate(100deg); /* Firefox */
        -ms-transform: rotate(100deg); /* IE */
        -webkit-transform: rotate(100deg); /* Safari and Chrome */
        -o-transform: rotate(100deg); /* Opera */
        transform:rotate(100deg);
    }
/*
  and rotate the amount of the pie that's showing.

  NOTE: add an extra degree to all but the final piece, 
  to fill in unsightly gaps.
*/
    .pie[data-value="30"]:BEFORE {
        -moz-transform: rotate(31deg); /* Firefox */
        -ms-transform: rotate(31deg); /* IE */
        -webkit-transform: rotate(31deg); /* Safari and Chrome */
        -o-transform: rotate(31deg); /* Opera */
        transform:rotate(31deg);
    }
    .pie[data-value="40"]:BEFORE {
        -moz-transform: rotate(41deg); /* Firefox */
        -ms-transform: rotate(41deg); /* IE */
        -webkit-transform: rotate(41deg); /* Safari and Chrome */
        -o-transform: rotate(41deg); /* Opera */
        transform:rotate(41deg);
    }
    .pie[data-value="260"]:BEFORE {
        -moz-transform: rotate(260deg); /* Firefox */
        -ms-transform: rotate(260deg); /* IE */
        -webkit-transform: rotate(260deg); /* Safari and Chrome */
        -o-transform: rotate(260deg); /* Opera */
        transform:rotate(260deg);
    }
/*
NOTE: you could also apply custom classes (i.e. .s0 .v30)
but if the CSS3 attr() function proposal ever gets implemented,
then all the above custom piece rules could be replaced with
the following:

.pie[data-start] {
   transform:rotate(attr(data-start,deg,0);
}
.pie[data-value]:BEFORE {
   transform:rotate(attr(data-value,deg,0);
}
*/
<!-- 
for each piece of the pie chart create one div and give it
a data-value attribute that represents the amount (in degrees) that
represents its total visible portion, and a data-start attribute
that matches the amount rotation for the starting  (the cumulative value amount of all the previous pieces).

 
-->
<div class="pie" data-start="0" data-value="30"></div>
<div class="pie highlight" data-start="30" data-value="30"></div>
<div class="pie" data-start="60" data-value="40"></div>
<div class="pie big" data-start="100" data-value="260"></div>

Source: https://codepen.io/AtomicNoggin/pen/fEish


0

在 Aaditya Pandey 的例子基础上,这是一个非常小的解决方案,只需要使用 CSS 就可以轻松使用:

.pie {
  width: 200px;
  height: 200px;
  background-image: conic-gradient(orange 50%, blue 50% 75%, red 75% 90%, green 90%);
  border-radius: 50%
}
<div class="pie"></div>


0

你可以使用conic-gradient,当你需要简单的东西时它很好用。但是它看起来不太好。

使用clip-pathtransform可以得到更平滑的饼图。只需要一点逻辑,在值超过50%时翻转颜色和变换即可。

.smooth-pie-2 {
  clip-path: circle(50%);
  position: relative;
  display: block;
  width: 100px;
  height: 100px;
  background-color: #005744;
  box-sizing: border-box;
}

.smooth-pie-2 .after,
.smooth-pie-2 .before {
  position: absolute;
  display: none;
  width: 100px;
  height: 100px;
  background-color: #00AF89;
  box-sizing: border-box;
}

@supports(clip-path: circle(50%)) {
  .smooth-pie-2 .after,
  .smooth-pie-2 .before {
    display: block;
  }
}


/* just for demo */

main {
  display: flex;
  gap: 2em;
  font-family: sans-serif;
  margin: 0 2em;
}

section {
  display: flex;
  gap: 1em;
  flex-flow: column;
  font-family: sans-serif;
}
<main>
  <section>
    <h2>Smooth</h2>
    <!-- x <= 50% -->
    <div class="smooth-pie-2" style="width: 100px; height: 100px; background-color: #005744;" title="33%">
      <div class="before" style="transform: rotate(0.33turn) translatex(50px); background-color: #00AF89;"></div>
      <div class="after" style="transform: translatex(-50px); background-color: #00AF89;"></div>
    </div>
    <!-- x > 50% -->
    <div class="smooth-pie-2" style="width: 100px; height: 100px; background-color: #00AF89;" title="66%">
      <div class="before" style="transform: translatex(50px); background-color: #005744;"></div>
      <div class="after" style="transform: rotate(0.66turn) translatex(-50px); background-color: #005744;"></div>
    </div>
  </section>
  <section>
    <h2>Gradient</h2>
    <div style="width: 100px; height: 100px; border-radius: 50%; border: 1px solid #00000080; background-image: conic-gradient(#005744 33%, #00AF89 0%);"></div>
  </section>
</main>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接