Je suis nouveau sur d3.js et j'essaie de faire un graphique à secteurs avec. J'ai un seul problème: je ne peux pas sortir mes étiquettes en dehors de mes arcs ... Les étiquettes sont positionnées avec arc.centroid
arcs.append("svg:text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
Qui peut m'aider avec ça?
Je peux résoudre ce problème - avec la trigonométrie :).
Voir violon: http://jsfiddle.net/nrabinowitz/GQDUS/
Fondamentalement, l'appel de arc.centroid(d)
renvoie un tableau [x,y]
. Vous pouvez utiliser le théorème de Pythagore pour calculer l'hypoténuse, qui est la longueur de la ligne allant du centre de la tarte au centre de gravité de l'arc. Ensuite, vous pouvez utiliser les calculs x/h * desiredLabelRadius
Et y/h * desiredLabelRadius
Pour calculer le x,y
Souhaité pour votre ancre d'étiquette:
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * labelr) + ',' +
(y/h * labelr) + ")";
})
Le seul inconvénient ici est que text-anchor: middle
N'est plus un excellent choix - vous feriez mieux de définir le text-anchor
En fonction de quel côté du gâteau nous sommes:
.attr("text-anchor", function(d) {
// are we past the center?
return (d.endAngle + d.startAngle)/2 > Math.PI ?
"end" : "start";
})
Spécifiquement pour les graphiques circulaires, la fonction d3.layout.pie()
formatera les données avec les attributs startAngle
et endAngle
. Le rayon peut être celui que vous désirez (à quelle distance du centre vous souhaitez placer l'étiquette).
La combinaison de ces informations avec un couple fonctions trigonométriques vous permet de déterminer les coordonnées x et y pour les étiquettes.
Considérez ceci Gist / block .
Concernant le positionnement x/y du texte, la magie est dans cette ligne (formatée pour la lisibilité):
.attr("transform", function(d) {
return "translate(" +
( (radius - 12) * Math.sin( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
", " +
( -1 * (radius - 12) * Math.cos( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
")";
})
((d.endAngle - d.startAngle) / 2) + d.startAngle
Nous donne notre angle (thêta) en radians.(radius - 12)
Est le rayon arbitraire que j'ai choisi pour la position du texte.-1 *
L'axe y est inversé (voir ci-dessous).Les fonctions trig utilisées sont: cos = adjacent / hypotenuse
Et sin = opposite / hypotenuse
. Mais il y a quelques choses que nous devons considérer pour que cela fonctionne avec nos étiquettes.
Cela gâche un peu les choses et a essentiellement pour effet d'échanger sin
et cos
. Nos fonctions trigonométriques deviennent alors: sin = adjacent / hypotenuse
Et cos = opposite / hypotenuse
.
En substituant les noms de variables, nous avons sin(radians) = x / r
et cos(radians) = y / r
. Après quelques manipulations algébriques, nous pouvons obtenir les deux fonctions en termes de x et y respectivement r * sin(radians) = x
et r * cos(radians) = y
. De là, branchez-les simplement dans l'attribut transform/translate.
Cela mettra les étiquettes au bon endroit, pour les rendre fantaisistes, vous avez besoin d'une logique de style comme celle-ci:
.style("text-anchor", function(d) {
var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
if ( (rads > 7 * Math.PI / 4 && rads < Math.PI / 4) || (rads > 3 * Math.PI / 4 && rads < 5 * Math.PI / 4) ) {
return "middle";
} else if (rads >= Math.PI / 4 && rads <= 3 * Math.PI / 4) {
return "start";
} else if (rads >= 5 * Math.PI / 4 && rads <= 7 * Math.PI / 4) {
return "end";
} else {
return "middle";
}
})
Cela rendra les étiquettes de 10 h 30 à 1 h 30 et de 4 h 30 à 7 h 30 ancrées au milieu (elles sont au-dessus et en dessous), les étiquettes de 1 : 30 h à 4 h 30 ancre à gauche (ils sont à droite), et les étiquettes de 7 h 30 à 10 h 30 ancre à droite (ils sont à droite) la gauche).
Les mêmes formules peuvent être utilisées pour n'importe quel graphique radial D3, la seule différence est la façon dont vous déterminez l'angle.
J'espère que cela aide quiconque à tomber dessus!
Merci!
J'ai trouvé une façon différente de résoudre ce problème, mais la vôtre semble meilleure :-)
J'ai créé un deuxième arc avec un rayon plus grand et l'ai utilisé pour positionner mes étiquettes.
///// Arc Labels /////
// Calculate position
var pos = d3.svg.arc().innerRadius(r + 20).outerRadius(r + 20);
// Place Labels
arcs.append("svg:text")
.attr("transform", function(d) { return "translate(" +
pos.centroid(d) + ")"; })
.attr("dy", 5)
.attr("text-anchor", "middle")
.attr("fill", function(d, i) { return colorL(i); }) //Colorarray Labels
.attr("display", function(d) { return d.value >= 2 ? null : "none"; })
.text(function(d, i) { return d.value.toFixed(0) + "%"});
Je ne sais pas si cela aide, mais j'ai pu créer des arcs où je place du texte, à la fois sur l'arc et juste à l'extérieur. Dans un cas, lorsque je place des amplitudes de l'arc dans les arcs, je fais pivoter le texte sur l'arc pour qu'il corresponde à l'angle de l'arc. Dans l'autre, où je place le texte en dehors de l'arc, il est simplement horizontal. Le code se trouve à: http://bl.ocks.org/229526
Mon meilleur,
Franc
oui bébé, c'est SOHCAHTOA
function coordinates_on_circle(hyp, angle){
var radian= angle * Math.PI / 180 //trig uses radians
return {
x: Math.cos(radian) * hyp, //adj = cos(r) * hyp
y: Math.sin(radian) * hyp //opp = sin(r) * hyp
}
}
var radius=100
var angle=45
coordinates_on_circle(radius, angle)
Le CoffeeScript suivant a fonctionné pour moi pour obtenir des étiquettes toujours à l'intérieur des tranches de tarte, mais vers le bord extérieur:
attr 'transform', (d) ->
radius = width / 2 # radius of whole pie chart
d.innerRadius = radius * 0.5
d.outerRadius = radius * 1.5
'translate(' + arc.centroid(d) + ')'
J'ai obtenu le même résultat en dessinant le pourcentage d'étiquettes en dehors du graphique à secteurs, voici le code http://bl.ocks.org/farazshuja/e2cb52828c080ba85da5458e2304a61f
g.append("text")
.attr("transform", function(d) {
var _d = arc.centroid(d);
_d[0] *= 2.2; //multiply by a constant factor
_d[1] *= 2.2; //multiply by a constant factor
return "translate(" + _d + ")";
})
.attr("dy", ".50em")
.style("text-anchor", "middle")
.text(function(d) {
if(d.data.percentage < 8) {
return '';
}
return d.data.percentage + '%';
});
C'était la réponse à faible coût dont j'étais satisfait. Il pousse toutes les étiquettes horizontalement (c'est là que j'avais l'espace supplémentaire):
g.append("text")
.attr("transform", function(d) {
var pos = arc.centroid(d);
return "translate(" + (pos[0] + (.5 - (pos[0] < 0)) * radius) + "," + (pos[1]*2) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", function(d) {
return arc.centroid(d)[0] > 0 ? "start" : "end";
})
.text(function(d) { return d.label; });