设计一个灵活的、可维护CSS和SVG饼图SVG
SVG解决方案
SVG做图形化任务变得简单,饼图自然也是可以的,各种复杂的效果都是可以做出来,这里主要是分享一些技巧。
让我们从一个简单的圆开始:
<svg width="100" height="100"> <circle r="30" cx="50" cy="50" /> </svg>
然后给这个圆附上一些简单的样式:
circle { fill: yellowgreen; stroke: #655; stroke-width: 30; }
注:你可能已经知道,CSS属性同样适用于SVG元素,考虑到可移植性,这将是会很方便的。
图九:我们的起点:一个绿色的SVG圆,填充色为#655
图九显示,SVG strokes不仅仅包含stroke和stroke-width属性。它还包含一些不是很常用的属性来微调strokes。stroke-dasharray就是其中之一。它是为了创建虚的strokes。例如,我们可以这样用:
stroke-dasharray: 20 10;
图十:一个简单的dashed stroke,由stroke-dasharray所创建
上面的CSS代码表示我们要虚实相间的圆(实线部分宽度为20,间隔部分宽度为10)。到现在为止,你可能想知道这个SVG与饼图究竟有啥关系?当我们把实线部分的宽度设置为0,间隔部分宽度设置为大于或等于圆的周长,这样就变得清晰了。(C = 2πr, so in our case C = 2π × 30 ≈ 189):
stroke-dasharray: 0 189;
图十一:多个不同stroke-dasharray值的效果图;从左到右依次为:0 189;40 149; 95 189; 150 189;
正如你所看到的,图十一的第一个SVG把所有的stroke部分移除了,我们只能看到绿色的圆。然而,好玩的是当我们开始逐渐增加stroke-dasharray属性的第一个值时,stroke部分慢慢回来了。由于间隙(gap)部分太大了,我们无法看到更多的实线部分笔画(stroke),仅仅只有一个stroke覆盖在圆上,看起来就像是我们所指定的圆的周长的百分比。
你可能已经开始试图去弄清楚究竟发生了什么:如果我们减小圆的半径到足够小,以至于完全被笔画(stroke)所覆盖,我们最终会得到类似于饼图的东西。例如,图十二,如果你将半径设置为25,并且将笔触的宽度设置为50会发生什么?下面的代码所呈现出来的图形如下:
图十二:我们的SVG慢慢的有点像饼图了
记住:SVG笔触总是一半在所对应的元素内,一半在元素外。将来我们将控制这一行为。
<svg width="100" height="100"> <circle r="25" cx="50" cy="50" /> </svg>
circle { fill: yellowgreen; stroke: #655; stroke-width: 50; stroke-dasharray: 60 158; /* 2π × 25 ≈ 158 */ }
现在,把它变成一个我们在之前的解决方案中所呈现的饼图就变得更简单了:我们只要在笔触的下一层画一个大的绿色的圆,然后顺时针旋转90°,这样,是他看起来像是从12点钟方向开始。由于<svg>元素也是一个HTML元素,我们同样可以给它写样式:
svg { transform: rotate(-90deg); background: yellowgreen; border-radius: 50%;}
图十三:最终的SVG饼图
你可以看到最终的效果图(图十三)。这种技术使得饼图从0到100%的动画效果更加简单。我们仅仅需要创建一个CSS动画,使得属性stroke-dasharray的值从0 158到158 158;
@keyframes fillup { to { stroke-dasharray: 158 158; } } circle { fill: yellowgreen; stroke: #655; stroke-width: 50; stroke-dasharray: 0 158; animation: fillup 5s linear infinite; }
作为额外的改进,我们可以指定圆的半径以使得它的周长为(无限接近于)100,这样我们就能方便的指定stroke-dasharray的长为百分比,而不需要去做额外的计算。由于周长=2πr,所以,半径r=100÷2π≈15.915494309,我们可以大约的认为是16。我们还将在viewBox属性中设置SVG的尺寸而不是直接设置它的width和height,以使其适应期容器的大小。
经过这些修改,图十三饼图将变成:
<svg viewBox="0 0 32 32"> <circle r="16" cx="16" cy="16" /> </svg>
svg { width: 100px; height: 100px; transform: rotate(-90deg); background: yellowgreen; border-radius: 50%; } circle { fill: yellowgreen; stroke: #655; stroke-width: 32; stroke-dasharray: 38 100; /* for 38% */ }
注意到了没有?现在设置百分比将变得非常简单。当然,即使已经变得如此简单了,我们仍然不愿意去重复设置每一个SVG饼图。是时候让JavaScript为我们提供一点点自动化的帮助的时候了。我们将写一小段脚本来接管如下的简单的HTML标记:
<div class="pie">20%</div> <div class="pie">60%</div>
并且在每个.pie元素中添加内联SVG,并附带所有的必要元素和属性。它还将添加一个<title>元素,以帮助读屏器用户也能知道饼图的百分比的值。最终的脚本如下:
$$('.pie').forEach(function(pie) { var p = parseFloat(pie.textContent); var NS = "http://www.w3.org/2000/svg"; var svg = document.createElementNS(NS, "svg"); var circle = document.createElementNS(NS, "circle"); var title = document.createElementNS(NS, "title"); circle.setAttribute("r", 16); circle.setAttribute("cx", 16); circle.setAttribute("cy", 16); circle.setAttribute("stroke-dasharray", p + " 100"); svg.setAttribute("viewBox", "0 0 32 32"); title.textContent = pie.textContent; pie.textContent = ''; svg.appendChild(title); svg.appendChild(circle); pie.appendChild(svg);});
这就是SVG的饼图。你可能会认为CSS的方法更好,因为它的代码更简单并且也更少的外来元素。然而,相对于纯CSS来说,SVG的方法也有一定的长处:
它非常容易添加第三种颜色:只要添加另外一种笔触,并用stroke-dashoffset设置笔触的偏移量。或者,将stroked长度添加到之前的圆的长度。你如何能够将第三种颜色添加到第一张解决方案做的饼图里呢?
我们不需要额外担心打印问题,因为SVG元素被认为是内容并打印,就像<img>元素一样。第一种方案取决于背景,因此不会打印。
我们能够通过内联样式来改变颜色,这意味着我们能够很方便的通过JavaScript脚本来改变(e.g.,取决于用户输入)。第一张方案依赖于伪元素,除了继承之外,它不支持内联样式,这并不总是方便的。
未来的饼图
锥形梯度在这里也将非常有帮助。饼图所需要的是一个圆形元素,具有两个颜色停止点的锥形渐变。 例如,图5中的40%饼图将简单如下:
.pie { width: 100px; height: 100px; border-radius: 50%; background: conic-gradient(#655 40%, yellowgreen 0); }
此外,一旦CSS Values Level 3中定义的更新的attr()函数被广泛实现,您将能够使用简单的HTML属性来控制百分比:
background: conic-gradient(#655 attr(data-value %), yellowgreen 0);
这也使得它非常容易添加第三种颜色。 例如,对于像上面上显示的饼图,我们只需添加两个颜色:
background: conic-gradient(deeppink 20%, #fb3 0, #fb3 30%, yellowgreen 0);
锥形梯度在这里也将非常有帮助。
这个饼图就基本做出来了,大家有问题吗? 同时大家学习可以加入我们的学习群 497187010 一起学习和交流哦 另有学习资料和 学习交流 解答大家的学习问题