Размещение меток в центре узлов в 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 динамически, так что если позиционирование метки меняется вместе с позицией круга автоматически. Концептуальное объяснение чрезвычайно приветствуется с примером кода.
Ответы
Ответ 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, когда дело касается текстовых элементов. Понимание основных механизмов и структур данных помогло мне лучше понять, как мне пришлось модифицировать код, чтобы он работал.