设计一个灵活的、可维护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 一起学习和交流哦 另有学习资料和 学习交流 解答大家的学习问题

    最后编辑于
    ©著作权归作者所有,转载或内容合作请联系作者
    • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
      沈念sama阅读 159,015评论 4 362
    • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
      沈念sama阅读 67,262评论 1 292
    • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
      开封第一讲书人阅读 108,727评论 0 243
    • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
      开封第一讲书人阅读 43,986评论 0 205
    • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
      茶点故事阅读 52,363评论 3 287
    • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
      开封第一讲书人阅读 40,610评论 1 219
    • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
      沈念sama阅读 31,871评论 2 312
    • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
      开封第一讲书人阅读 30,582评论 0 198
    • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
      沈念sama阅读 34,297评论 1 242
    • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
      茶点故事阅读 30,551评论 2 246
    • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
      茶点故事阅读 32,053评论 1 260
    • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
      沈念sama阅读 28,385评论 2 253
    • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
      茶点故事阅读 33,035评论 3 236
    • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
      开封第一讲书人阅读 26,079评论 0 8
    • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
      开封第一讲书人阅读 26,841评论 0 195
    • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
      沈念sama阅读 35,648评论 2 274
    • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
      茶点故事阅读 35,550评论 2 270

    推荐阅读更多精彩内容