Подтвердить что ты не робот

Размещение меток в центре узлов в d3.js

Я начинаю с d3.js и пытаюсь создать строку узлов, каждая из которых содержит метку с центрированным номером.

Я могу визуально получить желаемый результат, но способ, которым я это сделал, вряд ли оптимален, поскольку он включает жесткое кодирование координат x-y для каждого текстового элемента. Ниже приведен код:

var svg_w = 800;
var svg_h = 400;
var svg = d3.select("body")
    .append("svg")
    .attr("width", svg_w)
    .attr("weight", svg_h);

var dataset = [];
for (var i = 0; i < 6; i++) {
    var datum = 10 + Math.round(Math.random() * 20);
    dataset.push(datum);
}

var nodes = svg.append("g")
               .attr("class", "nodes")
               .selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("class", "node")
               .attr("cx", function(d, i) {
                   return (i * 70) + 50;
               })
               .attr("cy", svg_h / 2)
               .attr("r", 20);

var labels = svg.append("g")
                .attr("class", "labels")
                .selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .attr("dx", function(d, i) {
                    return (i * 70) + 42
                })
                .attr("dy", svg_h / 2 + 5)
                .text(function(d) {
                    return d;
                });

Класс node - это пользовательский класс CSS, который я определил отдельно для элементов circle, тогда как классы nodes и labels явно не определены и заимствованы из этого ответ.

Как видно, позиционирование каждой текстовой метки жестко закодировано так, что оно появляется в центре каждого node. Очевидно, это неправильное решение.

Мой вопрос заключается в том, как я должен правильно связать каждую текстовую метку с каждым кругом node динамически, так что если позиционирование метки меняется вместе с позицией круга автоматически. Концептуальное объяснение чрезвычайно приветствуется с примером кода.

4b9b3361

Ответ 1

Атрибут text-anchor работает как ожидалось в элементе svg, созданном D3. Однако вам нужно добавить text и circle в обычный элемент g, чтобы убедиться, что text и circle расположены по центру друг от друга.

Чтобы сделать это, вы можете изменить свою переменную nodes на:

var nodes = svg.append("g")
           .attr("class", "nodes")
           .selectAll("circle")
           .data(dataset)
           .enter()
           // Add one g element for each data node here.
           .append("g")
           // Position the g element like the circle element used to be.
           .attr("transform", function(d, i) {
             // Set d.x and d.y here so that other elements can use it. d is 
             // expected to be an object here.
             d.x = i * 70 + 50,
             d.y = svg_h / 2;
             return "translate(" + d.x + "," + d.y + ")"; 
           });

Обратите внимание, что dataset теперь представляет собой список объектов, поэтому вместо простого списка строк можно использовать d.y и d.x.

Затем замените код circle и text append следующим образом:

// Add a circle element to the previously added g element.
nodes.append("circle")
      .attr("class", "node")
      .attr("r", 20);

// Add a text element to the previously added g element.
nodes.append("text")
     .attr("text-anchor", "middle")
     .text(function(d) {
       return d.name;
      });

Теперь вместо изменения позиции circle вы измените положение элемента g, который перемещает как circle, так и text.

Вот JSFiddle, показывающий центрированный текст в кругах.

Если вы хотите, чтобы ваш текст находился в отдельном элементе g, чтобы он всегда отображался сверху, используйте значения d.x и d.y, установленные в первом элементе g для transform текст.

var text = svg.append("svg:g").selectAll("g")
         .data(force.nodes())
         .enter().append("svg:g");

text.append("svg:text")
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; });

text.attr("transform",  function(d) {
      return "translate(" + d.x + "," + d.y + ")"; 
    });

Ответ 2

Лучший ответ пришел от самого искателя:

просто дальнейшее наблюдение: только с .attr( "text-anchor", "middle" ) для каждого текстового элемента метка находится на середине горизонтально, но слегка от вертикали. Я исправил это, добавив attr ( "y", ".3em" ) (заимствованные из примеров на веб-сайте d3.js), который, кажется, работает хорошо даже для произвольного размера круга node. Однако, что именно это дополнительный атрибут ускользает от моего понимания. Конечно, это что-то для y-координаты каждого текстового элемента, но почему .3em в конкретный? Мне кажется почти волшебным...

Просто добавьте .attr("text-anchor", "middle") к каждому текстовому элементу.

Пример:

node.append("text")
    .attr("x", 0)
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; });

Ответ 3

Эта страница описывает, что происходит под капотом svg, когда дело касается текстовых элементов. Понимание основных механизмов и структур данных помогло мне лучше понять, как мне пришлось модифицировать код, чтобы он работал.