Reputation: 385
I'm having an issue with a D3 pie chart where labels are cutoff when they appear. Here's a pic:
I'm new to D3, and am not sure exactly where a fix should be. The logic for making the pie chart is 400 lines, so I made a pastebin: https://pastebin.com/32CxpeDM
Maybe the issue is in this function?
function showDetails(layer, data) {
active.inner = !active.inner
active.outer = false
let lines = guideContainer.selectAll('polyline.guide-line-inner')
.data(pie(data.inner))
let text = guideContainer.selectAll('.inner-text')
.data(pie(data.inner))
if (active.inner) {
// LINES
lines.enter()
.append('polyline')
.attr('points', calcPoints)
.attr('class', 'guide-line-inner')
.style('opacity', 0)
.transition().duration(300)
.style('opacity', d => { return d.data.value <= 0 ? 0 : 1 })
lines.attr('points', calcPoints).attr('class', 'guide-line-inner')
// TEXT
text.enter()
.append('text')
.html(labelText)
.attr('class', 'inner-text label label-circle')
.attr('transform', labelTransform)
.attr('dy', '1.1em')
.attr('x', d => { return (midAngle(d) < Math.PI ? 15 : -15) })
.style('text-anchor', d => { return (midAngle(d)) < Math.PI ? 'start' : 'end' })
.style('opacity', 0)
.call(wrap, 300)
.on('click', d => { vm.$emit('details', d) })
.transition().duration(300)
.style('opacity', d => { return d.data.value <= 0 ? 0 : 1 })
} else {
lines.transition().duration(300)
.style('opacity', 0)
.remove()
text.transition().duration(300)
.style('opacity', 0)
.remove()
}
guideContainer.selectAll('polyline.guide-line-outer').transition().duration(300)
.style('opacity', 0)
.remove()
guideContainer.selectAll('.outer-text').transition().duration(300)
.style('opacity', 0)
.remove()
}
Like I said, I don't know D3 so I'm not sure what my options are for fixing this. Make the chart smaller, fix some problem with its div container, send it to the front, or edit the above code. Ideally this would be a clean fix and not a hack, which is what I've been trying.
What the easiest and cleanest fix for this problem? Thanks!
Upvotes: 3
Views: 2004
Reputation: 28583
One approach that would conserve space would be to use d3 tooltips instead of fixed-position labels as in this fiddle (created by another jsfiddle user, I just added the margin
)
Javascript:
//Width/height
var w = 300;
var h = 300;
var dataset = [5, 10, 20, 45, 6, 25];
var outerRadius = w / 2;
var innerRadius = w / 3;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie();
var color = d3.scale.category10();
//Create SVG element
var svg = d3.select("#vis")
.append("svg")
.attr("width", w)
.attr("height", h);
//Set up groups
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
//Draw arc paths
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
//Labels
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
//
var tip = d3.tip()
.attr("class", "d3-tip")
.html(function(d, i) {
return d.value
})
svg.call(tip)
arcs
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
This uses a d3 tip library by Justin Palmer, that can be found on Github.
Also, you could consider making the circle smaller?
Hope this helps
Upvotes: 0
Reputation: 3166
It looks like your labels are getting transformed past the edge of your SVG boundary.
I would try to translate the label elements farther left in this function:
function labelTransform (d) {
var pos = guideArc.centroid(d)
pos[0] = (radius + 100) * (midAngle(d) < Math.PI ? 1 : -1)
return 'translate(' + pos + ')'
}
You'll have to chase down where the label line is being generated and shorten that as well.
You might also try braking the label text into more lines in this function:
function labelText (d) {
if ((radius + 100) * (midAngle(d) < Math.PI ? 1 : 0)) {
return '<tspan>' + d.data.display_name + ' </tspan><tspan x="15">' + d3.format('($,.0f')(d.data.value) + '</tspan>'
} else {
return '<tspan>' + d.data.display_name + ' </tspan><tspan x="-15" text-anchor="end">' + d3.format('($,.0f')(d.data.value) + '</tspan>'
}
}
Upvotes: 2