2012-11-05 12 views
60

Używam D3 do generowania wykresu słupkowego (dostosowałem kod z this example). Etykiety, których używam na jednej osi, mają po kilka słów, a ponieważ powoduje to nakładanie się wszystkich etykiet, muszę przerwać te etykiety wzdłuż linii. (To będzie w porządku, jeśli mogę wymienić wszystkich miejsc w każdej etykiety ze znakami nowej linii.)Jak dodać nowe linie do etykiet na wykresach D3?

początkowo próbował to poprzez zastąpienie spacje z dosłownych nowymi liniami (&#xA;) i ustawienie xml:space="preserve" na <text> elementów etykiety. Niestety okazuje się, że SVG nie respektuje tej właściwości. Następnie próbowałem zawijać każde słowo w stylu <tspan>, które mogłem później stylizować. Zdałem każdą etykietę za pośrednictwem tej funkcji:

function (text) { 
    return '<tspan>' + text.replace(/ /g, '</tspan><tspan>') + '</tspan>'; 
} 

ale to właśnie stawia dosłownych <tspan> s do wyjścia. Jak mogę zawinąć etykiety tekstowe w pliku tspan (lub zrobić coś innego), aby moje etykiety się nie pokrywały?

+0

Czy to jest zduplikowane pytanie? http://stackoverflow.com/questions/4991171/auto-line-wrapping-in-svg-text –

+1

@PaulArmstrong naprawdę nie, OP musi wstawić 'tspan''s nie autowrap tekst z foreignObject (który jest przesadą i nieobsługiwany przez IE9 (i 10?). – Duopixel

Odpowiedz

81

skończyło się stosując następujący kod do złamania każdego x -działający etykietę całej linii:

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); 

pamiętać, że ta zakłada, że ​​etykiety zostały już utworzone. (Jeśli podążysz za the canonical histogram example, etykiety zostaną ustawione tak, jak potrzebujesz.) Nie ma też prawdziwej logiki przełamującej linię; funkcja przekształca każdą spację w znak nowej linii. To pasuje do moich celów, ale może być konieczne edytowanie linii split(), aby być mądrzejszym o tym, jak dzieli części linii na linie.

+5

To było dokładnie to, czego potrzebowałem, dziękuję. Dla tych, którzy mogą być w podobnej sytuacji, powinienem zwrócić uwagę, że 'd' tutaj jest punktem danych i * nie * ciąg znaków, który formatujesz, więc '.split()' (przynajmniej dla mnie) wymagało zmiany na 'd.description.split (" \ n ");'. –

+1

Całkiem ładnie wykonane. – aendrew

+0

wiele ... działa doskonale ... – GSD

7

SVG element tekstowy nie obsługuje tekstu owijanie, więc są dwie opcje:

  • podzielić tekst na wiele elementów tekstowych SVG
  • użyć div overlay HTML na górze SVG

Zobacz komentarz Mike Bostock na temat tego here.

+0

dzięki za link do komentarza – ptim

-1

użycie <tspan>

aw 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; 
      }); 

zmiana wszystkich wystąpień (.text do .html (

0

Istnieje również odpowiedź this na w rapowanie długich etykiet.

<!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> 

a plik danych "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 
6

Coś Znalazłem być przydatna jest przy użyciu znacznika 'foreignObject' zamiast tekstu lub elementów tspan. Pozwala to na proste osadzanie HTML, pozwalając na naturalne łamanie słów.Zastrzeżenie jest całkowite wymiary specyficznych potrzeb przedmiotem obrad:

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>'); 

Cokolwiek elementy umieścić wewnątrz tego obiektu można później uzyskano stosując d3.select/selectAll zaktualizować wartości tekstowe dynamicznie, jak również.

1

Po rozejrzeniu się stwierdziłem, że Mike Bostock dostarczył rozwiązanie umożliwiające owinięcie tekstu.

http://bl.ocks.org/mbostock/7555321

Aby wdrożyć go na moim kodu (używam zwinięty schemat drzewa). Po prostu skopiowałem metodę "wrap".

Potem dołączone następujące

// 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); 

Nie widzę żadnego powodu, nie powinno to działać na siły skierowane, baru lub innego wzoru

Poprawka:

mam zmodyfikował funkcję wrap do poniższych dla każdego, kto to czyta i używa rozwijanego wykresu. Zmiana w atrybucie "x" ustawia poprawnie przypisanie, inkrementacja płótna lnianego została przeprowadzona w oddzielnej linii, ponieważ problemy zostały odnotowane w oryginalnym kodzie, a "y" zostało ustawione na zero, w przeciwnym razie wystąpiłyby problemy, w których odstępy między wierszami wzrosłyby o każda linia.

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); 
      } 
     } 
    }); 
} 
Powiązane problemy