After playing a while with d3.js and looking at a lot of examples, I was able to draw multiple arcs. Each one starting and ending at an specific degree and and given radius.
var dataset = {
"2":[{"degree1":0, "degree2":1.5707963267949,
"label":"Sample Text Test"},
{"degree1":1.5707963267949, "degree2":3.1415926535898,
"label":"Lorem ipsum sample text"},
{"degree1":3.1415926535898, "degree2":4.7123889803847,
"label":"Sample Text Text"},
{"degree1":4.7123889803847, "degree2":6.2831853071796,
"label":"Lorem ipsum"}],
"1":[{"degree1":0, "degree2":3.1415926535898,
"label":"Sample"},
{"degree1":3.1415926535898, "degree2":6.2831853071796,
"label":"Text"}],
"0":[{"degree1":0, "degree2":6.2831853071796,
"label":""}]
},
width = 450,
height = 450,
radius = 75;
// Helper methods
var innerRadius = function(d, i, j) {
return 1 + radius * j;
};
var outerRadius = function(d, i, j) {
return radius * (j + 1);
};
var startAngle = function(d, i, j) {
return d.data.degree1;
};
var endAngle = function(d, i, j) {
return d.data.degree2;
};
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.startAngle(startAngle)
.endAngle(endAngle);
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width >> 1) + ',' + (height >> 1) + ')');
var level = svg.selectAll('g')
.data(function(d) {
return d3.values(dataset);
})
.enter()
.append('g');
var entry = level.selectAll('g')
.data(function(d, i) {
return pie(d);
})
.enter()
.append('g');
entry.append('path')
.attr('fill', '#aaa')
.attr('d', arc)
.attr('id', function(d, i, j) {
return 'arc' + i + '-' + j;
});
var label = entry.append('text')
.style('font-size', '20px')
.attr('dx', function(d, i, j) {
return Math.round((d.data.degree2 - d.data.degree1) * 180 / Math.PI);
})
.attr('dy', function(d, i, j) {
return ((radius * (j + 1)) - (1 + radius * j)) >> 1;
});
label.append('textPath')
.attr('xlink:href', function(d, i, j) {
return '#arc' + i + '-' + j;
})
.style('fill', '#000')
.text(function(d) {
return d.data.label;
});
See http://jsfiddle.net/3FP6P/2/ :
But some problem still exists:
Vertical alignment
You can use another arc with radius (innerRadius + outerRadius) / 2
and use it as the textPath
for the labels.
Note that even if you set the innerRadius == outerRadius
, D3 will draw an path which moves clockwise and then anti-clockwise to doubles over itself. This becomes important while trying to figure out the horizontal centre of the path: it is at the 25%
and 75%
points while 0%
and 50%
points lie on the two tips of the arc.
Horizontal alignment
Use text-anchor: middle
on the text element and set startOffset
to 25%
(or 75%
) on the textPath
.
Demo.
This is a more robust way than calculating the dx
and dy
by hand.
You should try out Lars's suggestions to further improve the quality and centring of the text, e.g. you might want to set text-rendering
to optimizeLegibility
and play with the baseline a bit.
Issues 2-4 are because of the font rendering. In my browser, the spacing and character size etc is consistent. You can try playing around with the text-rendering
attribute to improve things.
To get the text centred, you will need to set the alignment-baseline
and/or dominant-baseline
attributes.
If this still doesn't give you the results you're looking for, try decreasing the font size. This may help because a slight rotation of a character will be less visible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With