My goal is to have an interactive chart which displays additional information on the arc next to the chart. The code below is not clean, but is functional and does (almost) everything I want it to.
Specifically, it draws a little arc-pie chart, when you hover over an arc it lightens the arc and draws a line from the center of that arc to the data area to the right where a spend and a description are listed.
HOWEVER, the code seems inelegant and the datapoint (text) it is displaying are all the same one. It doesn't seem to be adding the additional data elements in order to display them, or it IS but is only ever selecting the first to display!
The HTML, which I haven't bothered to copy in is just a blank web page with a single div: <div id="pie">1,2,3</div>
var format = d3.format(",.2f");
var data = [
{'spend': 15, 'description': 'Controlled'},
{'spend': 3, 'description': 'Data Usage'},
{'spend': 21, 'description': 'International Roaming'}
];
var kulor = d3.scale.ordinal()
.range(['#648631','#D84B4B','#A05AE0']);
var width = 160,
height = 80,
radius = height / 2 - 10, // how wide is the circle within the box?
innerRadius = radius - 10, // how thich is the donut (the -# is the thickness)
cornerRadius = 3, // how wide are the curves of each section
padAngle = .07, // distance between items (in angles)
startLine = [40, -25]; // where the description line starts
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(radius)
.cornerRadius(cornerRadius);
var pie = d3.layout.pie()
.padAngle(padAngle)
.value(function (d) {
return d.spend;
});
var arcs = d3.select('#pie').html('').append('svg')
.data([data])
.attr('width', width)
.attr('height', height)
.append('g')
// keep centered but left
.attr('transform', 'translate(' + height / 2 + ',' + height / 2 + ')');
arcs.selectAll('path')
.data(pie).enter()
.append('path')
.style('fill', function(d, i) { return kulor(i); })
.attr('d', arc);
arcs.selectAll('line')
.data(pie).enter()
.append('line')
.classed('slice', true)
.attr('lineNum', function(d,i){ return 'sl'+i; })
.attr('x1', function(d, i) {
return arc.centroid(d, i)[0];
})
.attr('y1', function(d, i) {
return arc.centroid(d, i)[1];
})
.attr('x2', startLine[0]).attr('y2', startLine[1])
.attr('stroke-width', 2).attr('stroke','#333')
.style('visibility', 'hidden');
// fixed line
arcs.append('line')
.classed('fixedLine', true)
.attr('x1', startLine[0]).attr('y1', startLine[1])
.attr('x2', 120).attr('y2', startLine[1])
.attr('stroke-width', 2).attr('stroke','#333')
.style('visibility', 'hidden');
// Spend
arcs.each(function(d, i) {
console.log(d[i].spend + " " + i);
arcs.append('text')
.classed('spend', true)
.text(function(d,i){
console.log(d);
return '$' + format(d[i].spend);
})
.attr('x', 80).attr('y', startLine[1] - 4)
.attr('font-size',12)
.attr('font-family','Helvetica, sans-serif')
.attr('text-anchor','middle')
.attr('font-weigt','bold')
.style('visibility', 'hidden');
})
// Spend Description
arcs.append('text')
.text(function(d,i){ return d[i].description; })
.attr('x', 80).attr('y', startLine[1] + 12)
.attr('font-size',12)
.attr('font-family','Helvetica, sans-serif')
.attr('text-anchor','middle')
.attr('font-weigt','bold')
.style('visibility', 'hidden');;
arcs.selectAll('path')
.on('mouseover', function(d, i) {
var index = i + 1;
var nodeSel = d3.select(this).style({opacity:'0.8'});
nodeSel.select('text').style({opacity:'1.0'});
d3.select('.slice:nth-of-type('+index+')').style('visibility', 'visible');
d3.select('.spend:nth-of-type('+index+')').style('visibility', 'visible');
d3.select('.fixedLine').style('visibility', 'visible');
d3.selectAll('text').style('visibility', 'visible');
})
.on('mouseout', function(d, i) {
var nodeSel = d3.select(this).style({opacity:'1.0'});
nodeSel.select('text').style({opacity:'0'});
arcs.selectAll('.slice').style('visibility', 'hidden');
arcs.selectAll('.fixedLine').style('visibility', 'hidden');
arcs.selectAll('text').style('visibility', 'hidden');
});
Problem1:
You don't have to add as many text as the number of arcs. So this is wrong.
arcs.each(function(d, i) {
console.log(d[i].spend + " " + i);
arcs.append('text')
.classed('spend', true)
.text(function(d,i){
console.log(d);
return '$' + format(d[i].spend);
})
.attr('x', 80).attr('y', startLine[1] - 4)
.attr('font-size',12)
.attr('font-family','Helvetica, sans-serif')
.attr('text-anchor','middle')
.attr('font-weigt','bold')
.style('visibility', 'hidden');
})
The right way is just add one text DOM like this (and to that DOM give the description on mouse over)
arcs.append('text')
.classed('spend', true)
.attr('x', 80).attr('y', startLine[1] - 4)
.attr('font-size',12)
.attr('font-family','Helvetica, sans-serif')
.attr('text-anchor','middle')
.attr('font-weigt','bold')
.style('visibility', 'hidden');
Problem 2:
You don't need to set the text
while making the text
DOM like this:
.classed('spend', true)
.text(function(d,i){
console.log(d);
return '$' + format(d[i].spend);
})
You need to add the description on mouse over like this
.on('mouseover', function(d, i) {
var index = i + 1;
var nodeSel = d3.select(this).style({opacity:'0.8'});
nodeSel.select('text').style({opacity:'1.0'});
d3.select('.slice:nth-of-type('+index+')').style('visibility', 'visible');
d3.select('.spend:nth-of-type('+index+')').style('visibility', 'visible');
d3.select('.fixedLine').style('visibility', 'visible');
d3.selectAll('text').style('visibility', 'visible');
//setting the description data to the text dom
d3.selectAll(".description").text(d.data.description);
//setting the spend data to the text dom
d3.selectAll(".spend").text('$' + format(d.data.spend))
});
Working code here.