Как включить новые строки в метки на диаграммах D3?


Я использую D3 для создания гистограммы (я адаптировал код из ). Метки, которые я использую на x-axis-это несколько слов длиной каждый, и поскольку это делает все метки перекрывающимися, мне нужно разбить эти метки по линиям. (Будет хорошо, если я смогу заменить все пробелы в каждой метке новыми строками.)

Я изначально попробовал это, заменив пробелы буквальными новыми строками ( ) и xml:space="preserve" на этикетках' <text> элементы. К сожалению, оказывается, что SVG не уважает это свойство. Затем я попытался обернуть каждое слово в тег <tspan> что я мог бы позже стиль. Я передал каждую метку через эту функцию:

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

но это просто ставит буквальный <tspan>s в выход. Как я могу обернуть мой текст в tspans (или сделать что-то еще), чтобы мои метки не перекрывались?

6 62

6 ответов:

Я в конечном итоге с помощью следующего кода, чтобы сломать каждый x-метка оси по линии:

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

заметим, что это предполагает, что метки уже были созданы. (Если вы будете следовать пример канонической гистограмме тогда метки будут настроены именно так, как вам нужно.) Также нет никакой реальной логики разрыва строки; функция преобразует каждое пространство в новую строку. Это отлично подходит для моих целей, но вам может потребоваться отредактировать split() линия чтобы быть умнее о том, как он разбивает части строки на строки.

текстовый элемент SVG не поддерживает перенос текста, поэтому есть два варианта:

  • разделить текст на несколько текстовых элементов SVG
  • использовать оверлей в формате HTML div на верхней части СВГ

смотрите комментарий Майка Бостока об этом здесь.

что-то, что я нашел полезным, использует тег "foreignObject" вместо элементов text или tspan. Это позволяет для простого встраивания HTML, что позволяет слова, чтобы сломать естественно. Оговоркой являются габаритные размеры объекта, отвечающие конкретным требованиям:

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

любые элементы, которые вы размещаете внутри этого объекта, могут быть позже получены с помощью d3.выберите/selectAll для динамического обновления текстовых значений.

осмотревшись, я обнаружил, что Майк Босток предоставил решение, позволяющее вам обернуть текст вокруг.

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

чтобы реализовать его в моем коде (я использую свернутую древовидную диаграмму). Я просто скопировал метод "wrap".

затем добавляется следующее

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

Я не вижу причин, по которым это не должно работать для направленного на силу, бара или любого другого шаблона

поправка :

Я изменил функцию wrap следующим образом для тех, кто читает это и использует collapisible graph. Изменение в атрибуте " x "устанавливает allignment правильно, увеличение linenumber было выполнено на отдельной строке, поскольку проблемы были отмечены в исходном коде, а" y " было жестко установлено в ноль, иначе возникнут проблемы, в которых межстрочный интервал увеличивается с каждой строкой.

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

там же этой ответ на обертывание длинных этикеток.

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

и файл данных " данные.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

использовать <tspan>

и в НВ.d3

nv.модели.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;
            });

изменить все вхождения .текст( к.html (