javascript - lineas - ¿Cómo incluyo las nuevas líneas en las etiquetas de los gráficos D3?
graficas en pagina web (6)
Algo que he encontrado útil es usar una etiqueta ''foreignObject'' en lugar de elementos text o tspan. Esto permite la simple incrustación de HTML, permitiendo que las palabras se rompan naturalmente. La advertencia es que las dimensiones generales del objeto satisfacen necesidades específicas:
var myLabel = svg.append(''foreignObject'')
.attr({
height: 50,
width: 100, // dimensions determined based on need
transform: ''translate(0,0)'' // put it where you want it...
})
.html(''<div class"style-me"><p>My label or other text</p></div>'');
Cualquiera que sea el elemento que coloque dentro de este objeto puede obtenerse luego utilizando d3.select/selectAll para actualizar los valores de texto de forma dinámica también.
Estoy usando D3 para generar un gráfico de barras (he adaptado el código de este ejemplo ). Las etiquetas que estoy usando en el eje x son un par de palabras largas cada una, y como esto hace que todas las etiquetas se superpongan, necesito romper estas etiquetas entre líneas. (Estará bien si puedo reemplazar todos los espacios en cada etiqueta con líneas nuevas).
Originalmente probé esto reemplazando los espacios con nuevas líneas literales ( 

) y estableciendo xml:space="preserve"
en los elementos de las etiquetas '' <text>
. Desafortunadamente, resulta que SVG no respeta esta propiedad. Luego traté de envolver cada palabra en un <tspan>
que luego podría <tspan>
estilo. Pasé cada etiqueta a través de esta función:
function (text) {
return ''<tspan>'' + text.replace(/ /g, ''</tspan><tspan>'') + ''</tspan>'';
}
pero esto simplemente pone literal <tspan>
s en la salida. ¿Cómo puedo envolver mis etiquetas de texto en tspan
s (o hacer otra cosa) para que mis etiquetas no se superpongan?
Después de haber mirado alrededor encontré que Mike Bostock ha proporcionado una solución que le permite ajustar el texto.
http://bl.ocks.org/mbostock/7555321
Para implementarlo en mi código (estoy usando un diagrama de árbol colapsado). Simplemente copié el método "wrap".
Luego se agregó lo siguiente
// Standard code for a node
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.text(function(d) { return d.text; })
// New added line to call the function to wrap after a given width
.call(wrap, 40);
No veo ninguna razón por la que esto no debería funcionar para una fuerza dirigida, barra o cualquier otro patrón
Enmienda:
Modifiqué la función de ajuste a lo siguiente para cualquier persona que lea esto y esté usando un gráfico plegable. El cambio en el atributo "x" establece la alineación correctamente, el aumento del número de lienzo se realizó en una línea separada ya que se anotaron problemas en el código original y "y" se estableció en cero. De lo contrario, se producirían problemas en el cada línea.
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(//s+/).reverse(),
word,
line = [],
lineNumber = 0,
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
lineHeight = 1.1, // ems
tspan = text.text(null).append("tspan").attr("x", function(d) { return d.children || d._children ? -10 : 10; }).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
var textWidth = tspan.node().getComputedTextLength();
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
++lineNumber;
tspan = text.append("tspan").attr("x", function(d) { return d.children || d._children ? -10 : 10; }).attr("y", 0).attr("dy", lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
El elemento de texto SVG no admite el ajuste de texto, por lo que hay dos opciones:
- dividir el texto en múltiples elementos de texto SVG
- usar un div de superposición de HTML en la parte superior del SVG
Ver el comentario de Mike Bostock sobre esto here .
También hay this respuesta para envolver etiquetas largas.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.title {
font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 80, right: 180, bottom: 80, left: 180},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, .3);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(8, "%");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("text")
.attr("class", "title")
.attr("x", x(data[0].name))
.attr("y", -26)
.text("Why Are We Leaving Facebook?");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, x.rangeBand());
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
});
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(//s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
function type(d) {
d.value = +d.value;
return d;
}
</script>
y el archivo de datos "data.tsv":
name value
Family in feud with Zuckerbergs .17
Committed 671 birthdays to memory .19
Ex is doing too well .10
High school friends all dead now .15
Discovered how to “like” things mentally .27
Not enough politics .12
Terminé usando el siguiente código para dividir cada etiqueta de eje x entre líneas:
var insertLinebreaks = function (d) {
var el = d3.select(this);
var words = d.split('' '');
el.text('''');
for (var i = 0; i < words.length; i++) {
var tspan = el.append(''tspan'').text(words[i]);
if (i > 0)
tspan.attr(''x'', 0).attr(''dy'', ''15'');
}
};
svg.selectAll(''g.x.axis g text'').each(insertLinebreaks);
Tenga en cuenta que esto supone que las etiquetas ya se han creado. (Si sigue el ejemplo del histograma canónico , las etiquetas se habrán configurado de la manera que necesita). Tampoco existe una lógica real de salto de línea; la función convierte cada espacio en una nueva línea. Esto encaja perfectamente con mis propósitos, pero es posible que necesite editar la línea de split()
para ser más inteligente acerca de cómo divide las partes de la cadena en líneas.
usa <tspan>
y en nv.d3
nv.models.axis = function () {
...
.select(''text'')
.attr(''dy'', ''0em'')
.attr(''y'', -axis.tickPadding())
.attr(''text-anchor'', ''middle'')
.text(function(d,i) {
var v = fmt(d);
return ('''' + v).match(''NaN'') ? '''' : v;
});
cambiar todas las apariciones de .text (a .html (