SVG / D3.js在饼图标签前添加图像

时间:2016-10-31 11:19:37

标签: javascript d3.js svg

我这里有一个简单的饼图:
https://jsfiddle.net/5dexn2kn/1/

现在我想在每个标签前面添加一个图标,如下所示:
enter image description here

我尝试将图像附加到每个标签组
https://jsfiddle.net/5dexn2kn/2/

正如您所看到的,因为text-anchor可能是startend,每个标签的文字长度也不同,我不知道如何查找和放置图像到正确的位置。

我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:1)

也许这会有所帮助:https://jsfiddle.net/5dexn2kn/3/

给文字一个id:

labelG.append('text')
  .attr('id', function(d) {
    return d.data.label + '_text'
  })

这必须是独一无二的。在这种情况下它可以工作,但如果你有时候有相同的标签,它就不会。

然后获取相应标签文本的宽度,如下所示:

var thisText = document.getElementById(d.data.label + '_text') //select based on data, as id above is given from data
var thisTextWidth = thisText.clientWidth;

然后使用相同的逻辑来确定您是希望文本锚点start还是end来确定图像的位置:

    if ((d.endAngle + d.startAngle) / 2 > Math.PI) {
      return -35 - thisTextWidth;
    } else {
      return -25;
    }

答案 1 :(得分:1)

您可以使用getBoundingBox()功能来实现此目的。

<强>代码

labelG.append('image')
  .attr('xlink:href', 'https://placekitten.com/20/20?.jpg')
  .attr('height', 20)
  .attr('width', 20)
  .attr("x", function(d) {
    var bbox = this.parentNode.getBBox();
    return bbox.x;
  })
  .attr("y", function() {
    var bbox = this.parentNode.getBBox();
    return bbox.y;
  });

labelG.selectAll("text").attr("dx", 25);

完整代码段

const width = 400
const height = 400
const labelSpace = 50
const donutRadius = Math.min(width, height) / 2
const maxRadius = donutRadius + labelSpace
const colorScheme = d3.scaleOrdinal(d3.schemeCategory20)
const innerRadius = 20
const svgTranslate = [width / 2 + labelSpace * 2, height / 2 + labelSpace * 2]

function getPercent(value, total) {
  return Math.round(value / total * 100)
}

const data = [{
  label: 'aaaaaaa',
  value: 19
}, {
  label: 'bbb',
  value: 31
}, {
  label: 'c',
  value: 31
}, {
  label: 'ddddddddddd',
  value: 8
}, {
  label: 'eeee',
  value: 10
}]

const total = data.map(d => d.value).reduce((a, b) => a + b)

const svg = d3.select('#donutchart')
  .append('svg')
  .attr('width', width + maxRadius)
  .attr('height', height + maxRadius)
  .append('g')
  .attr('transform', `translate(${svgTranslate[0]}, ${svgTranslate[1]})`)

const arc = d3.arc()
  .innerRadius(innerRadius)
  .outerRadius(donutRadius)

const donut = d3.pie()
  .sort(null)
  .value(d => d.value)

const arcG = svg.selectAll('g.arc-g')
  .data(donut(data))
  .enter()
  .append('g')

arcG.append('path')
  .attr('d', arc)
  .attr('fill', d => colorScheme(d.data.label))

const labelG = arcG.append('g')
  .attr('transform', d => {
    const c = arc.centroid(d)
    const x = c[0] * 2
    const y = c[1] * 2
    return `translate(${x}, ${y})`
  })

labelG.append('text')
  .attr('dy', '.35em')
  .html(d =>
    `${d.data.label}<tspan class="label-percent"> ${getPercent(d.data.value, total)}%</tspan>`
  )
  .attr('text-anchor', d =>
    (d.endAngle + d.startAngle) / 2 > Math.PI ? 'end' : 'start'
  )

labelG.append('image')
  .attr('xlink:href', 'https://placekitten.com/20/20?.jpg')
  .attr('height', 20)
  .attr('width', 20)
  .attr("x", function(d) {
    var bbox = this.parentNode.getBBox();
    return bbox.x;
  })
  .attr("y", function() {
    var bbox = this.parentNode.getBBox();
    return bbox.y;
  });

labelG.selectAll("text").attr("dx", 25);
body {
  font-family: San Francisco Display, sans-serif;
}
.label-percent {
  font-weight: bold;
}
<div id="donutchart"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>