d3饼图

时间:2016-08-25 23:04:23

标签: javascript d3.js svg rendering

我正在尝试在http://bl.ocks.org/mbostock/3887235处跟随d3的饼图示例。

我的代码的最小样本:

const container = $('#graph-pie');
const width = container.width() * 0.5;
const height = 256;
const radius = Math.min(width, height) / 2;

const color = d3.scaleOrdinal()
    .range(['#869099', '#8c7853', '#007d4a']);

const arc = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(0);

const labelArc = d3.arc()
    .outerRadius(radius - 40)
    .innerRadius(radius - 40);

const pie = d3.pie()
    .sort(null)
    .value((d) => { return d.value });

const svg = d3.select('#graph-pie').append('svg')
    .attr('width', width)
    .attr('height', height)
.append('g')
    .attr('transform', 'translate(' + width/2 + ',' + height/2 + ')');

const g = svg.selectAll('.arc')
    .data(pie(totals))
    .enter()
.append('g')
    .attr('class', 'arc');

g.append('path')
    .attr('d', arc)
    .style('fill', (d) => { return color(d.data.name) });

g.append('text')
    .attr('transform', (d) => { return 'translate(' + labelArc.centroid(d) + ')' })
    .attr('dy', '.35em')
    .text((d) => {
        return names[d.data.name];
    });

问题在于,有时标签的文本会隐藏在饼图片后面。我注意到这种情况主要发生在Chrome上,但我不知道它在其他浏览器中没有发生。几个例子:

Example 1

Example 2

Example 3

如果有帮助,输出的SVG代码如下:

<div id="graph-pie" class="graph pie">
  <svg width="668" height="256">
    <g transform="translate(334,128)">
      <g class="arc">
        <path d="M7.225416114969383e-15,-118A118,118,0,0,1,98.56840842206363,-64.87117126383073L0,0Z" style="fill: rgb(134, 144, 153); z-index: 10;">
        <text transform="translate(41.753394753534344,-77.46388853237056)" dy=".35em">Sensitive</text>
      </g>
      <g class="arc">
        <path d="M98.56840842206363,-64.87117126383073A118,118,0,0,1,-27.95807360901471,114.64007205193529L0,0Z" style="fill: rgb(140, 120, 83);">
        <text transform="translate(71.92851960216296,50.69800851947991)" dy=".35em">Somewhat Sensitive</text>
      </g>
      <g class="arc">
        <path d="M-27.95807360901471,114.64007205193529A118,118,0,0,1,-2.167624834490815e-14,-118L0,0Z" style="fill: rgb(0, 125, 74);">
        <text transform="translate(-87.37132713286675,-10.500056906587716)" dy=".35em">Tolerant</text>
      </g>
    </g>
  </svg>
</div>

我已经尝试过使用z-index CSS样式,但它没有任何效果,并且它看起来不像SVG元素具有任何类似的属性。如何强制我的文本元素在我的路径上呈现?

1 个答案:

答案 0 :(得分:0)

某些路径涵盖了文本,因为在您的代码中,每个g(组)都有一个路径元素和一个文本元素。因此,在给定组之后的组内路径(以SVG顺序,&#34;在它上面#34;)将在该特定文本之后呈现,覆盖它。 &#34;绘图顺序&#34;在这篇文章的底部描述了SVG。

因此,您需要做的是在渲染所有路径后渲染文本。有几种方法可以做到这一点,但最简单的方法(肯定不是最优雅的方法)是为文本创建另一组。

例如,这是您现在的代码。你可以看到一些文章被一些片段覆盖:

&#13;
&#13;
const width = 400
const height = 400;
const radius = Math.min(width, height) / 2.5;

const totals = [{"name":"Category A long label", "value":20}, 
		          {"name":"Category B long label", "value":50}, 
		          {"name":"Category C long label", "value":30},
							{"name":"Category D long label", "value":20}, 
		          {"name":"Category E long label", "value":50}, 
		          {"name":"Category F long label", "value":30}];

const color = d3.scaleOrdinal()
    .range(['#869099', '#8c7853', '#007d4a']);

const arc = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(0);

const labelArc = d3.arc()
    .outerRadius(radius - 40)
    .innerRadius(radius - 40);

const pie = d3.pie()
    .sort(null)
    .value((d) => { return d.value });

const svg = d3.select('#graph-pie').append('svg')
    .attr('width', width)
    .attr('height', height)
.append('g')
    .attr('transform', 'translate(' + width/2 + ',' + height/2 + ')');

const g = svg.selectAll('.arc')
    .data(pie(totals))
    .enter()
.append('g')
    .attr('class', 'arc');

g.append('path')
    .attr('d', arc)
    .style('fill', (d) => { return color(d.data.name) });

g.append('text')
	.attr("text-anchor", "middle")
    .attr('transform', (d) => { return 'translate(' + labelArc.centroid(d) + ')' })
    .attr('dy', '.35em')
    .text((d) => {
        return d.data.name;
    });
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="graph-pie"></div>
&#13;
&#13;
&#13;

这是相同的代码,但是对于文本有不同的组,在路径之后呈现

&#13;
&#13;
const width = 400
const height = 400;
const radius = Math.min(width, height) / 2.5;

const totals = [{"name":"Category A long label", "value":20}, 
		          {"name":"Category B long label", "value":50}, 
		          {"name":"Category C long label", "value":30},
							{"name":"Category D long label", "value":20}, 
		          {"name":"Category E long label", "value":50}, 
		          {"name":"Category F long label", "value":30}];

const color = d3.scaleOrdinal()
    .range(['#869099', '#8c7853', '#007d4a']);

const arc = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(0);

const labelArc = d3.arc()
    .outerRadius(radius - 40)
    .innerRadius(radius - 40);

const pie = d3.pie()
    .sort(null)
    .value((d) => { return d.value });

const svg = d3.select('#graph-pie').append('svg')
    .attr('width', width)
    .attr('height', height)
.append('g')
    .attr('transform', 'translate(' + width/2 + ',' + height/2 + ')');

const g = svg.selectAll('.arc')
    .data(pie(totals))
    .enter()
.append('g')
    .attr('class', 'arc');
		
const g2 = svg.selectAll('.arc2')
    .data(pie(totals))
    .enter()
.append('g')
    .attr('class', 'arc');
		

g.append('path')
    .attr('d', arc)
    .style('fill', (d) => { return color(d.data.name) });

g2.append('text')
	.attr("text-anchor", "middle")
    .attr('transform', (d) => { return 'translate(' + labelArc.centroid(d) + ')' })
    .attr('dy', '.35em')
    .text((d) => {
        return d.data.name;
    });
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="graph-pie"></div>
&#13;
&#13;
&#13;

你可以看到,现在,没有任何文字被覆盖。

这很有效,但正如我所说,它并不是最优雅的方法,因为如果你检查SVG,你会看到一堆具有单一元素的群体(这没有任何意义) 。另一种方法是独立创建路径和文本,这需要稍微修改一下代码。

SVG:绘图顺序

这可能令人沮丧:您使用D3.js进行可视化,但您想要在顶部的矩形隐藏在另一个矩形后面,或者您计划在某个圆圈后面的线实际上是在它上面。您尝试使用CSS中的 z-index 解决此问题,但它不起作用(在SVG 1.1中)。

解释很简单:在SVG中,元素的顺序定义了&#34;绘画&#34;的顺序,绘画的顺序定义了谁是最重要的。

  

SVG文档片段中的元素具有隐式绘制顺序,SVG文档片段中的第一个元素得到&#34;绘制&#34;第一。后续元素绘制在先前绘制的元素之上。

所以,假设我们有这个SVG:

<svg width="400" height=200>
    <circle cy="100" cx="80" r="60" fill="blue"></circle>
    <circle cy="100" cx="160" r="60" fill="yellow"></circle>
    <circle cy="100" cx="240" r="60" fill="red"></circle>
    <circle cy="100" cx="320" r="60" fill="green" z-index="-1"></circle>
</svg>
他有四个圆圈。蓝圈是第一个&#34;画&#34;,所以它将落后于其他所有。然后我们有黄色的那个,然后是红色的,最后是绿色的。绿色的是最后一个,它将位于顶部。

它的外观如下:

enter image description here

使用D3更改SVG元素的顺序

那么,是否可以改变元素的顺序?我可以在绿色圆圈前面制作红色圆圈吗?

是。您需要考虑的第一种方法是代码中的行的顺序:首先绘制背景元素,然后在代码中绘制前景元素。

但是我们可以动态地改变元素的顺序,即使它们被绘制后也是如此。您可以编写几个简单的JavaScript函数来执行此操作,但D3已经有2个不错的功能,selection.raise()selection.lower()

根据API:

  

selection.raise():按顺序重新插入每个选定元素作为其父元素的最后一个子元素。   selection.lower():按顺序重新插入每个选定元素作为其父元素的第一个子元素。

因此,为了展示如何操纵我们之前SVG中元素的顺序,这里有一个非常小的代码:

d3.selectAll("circle").on("mouseover", function(){
    d3.select(this).raise(); 
});

它做什么?它选择所有圆圈,当用户将鼠标悬停在一个圆圈上时,它会选择该特定圆圈并将其带到前面。很简单!

here is the JSFiddle和实时代码。