Аккорд d3: текст по центру
Я использую пример диаграммы аккордов d3 Эндрю и хочу центрировать все текстовые метки внутри изогнутого среза. Я перепробовал много вещей, но так и не смог центрировать тексты. Знаете ли вы, какой там трюк Wizzard нужен?
var width = 720,
height = 720,
outerRadius = Math.min(width, height) / 2 - 10,
innerRadius = outerRadius - 24;
var formatPercent = d3.format(".1%");
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var layout = d3.layout.chord()
.padding(.04)
.sortSubgroups(d3.descending)
.sortChords(d3.ascending);
var path = d3.svg.chord()
.radius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("id", "circle")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.append("circle")
.attr("r", outerRadius);
d3.csv("ex_csv.csv", function(cities) {
d3.json("ex_json.json", function(matrix) {
// Compute the chord layout.
layout.matrix(matrix);
// Add a group per neighborhood.
var group = svg.selectAll(".group")
.data(layout.groups)
.enter().append("g")
.attr("class", "group")
.on("mouseover", mouseover);
// Add the group arc.
var groupPath = group.append("path")
.attr("id", function(d, i) { return "group" + i; })
.attr("d", arc)
.style("fill", function(d, i) { return cities[i].color; });
// Add a text label.
var groupText = group.append("text")
.attr("x", 6)
.attr("dy", 15);
groupText.append("textPath")
.attr("xlink:href", function(d, i) { return "#group" + i; })
.text(function(d, i) { return cities[i].name; });
// Remove the labels that don't fit. :(
groupText.filter(function(d, i) { return groupPath[0][i].getTotalLength() / 2 - 16 < this.getComputedTextLength(); })
.remove();
// Add the chords.
var chord = svg.selectAll(".chord")
.data(layout.chords)
.enter().append("path")
.attr("class", "chord")
.style("fill", function(d) { return cities[d.source.index].color; })
.attr("d", path);
}
});
});
</script>
2 ответа
Кроме того, я хотел бы предложить перейти на v4, документация для v2 практически отсутствует и с ней очень трудно справиться.
Вы можете установить как text-anchor
и startOffset
свойство для достижения того, что вы ищете.
Во-первых, вы хотите установить text-anchor
в middle
поскольку легче указать среднюю точку, чем найти среднюю точку и вернуться к поиску места, где должен начинаться текст.
Во-вторых, вам нужно установить startOffset
, Обратите внимание, что если вы используете 50%, текст не будет отображаться там, где вы хотите, так как общая длина пути для текста равна всем сторонам замкнутого цикла (якорной якоря), к которому вы добавляете. Установка его на 25 % будет работать, если у вас не будет другого внешнего и внутреннего радиуса. Но, поскольку у вас есть внешний радиус, который на 24 пикселя больше внутреннего радиуса, вы можете попробовать что-то вроде этого, чтобы вычислить количество пикселей, которое вам нужно, чтобы сместить центр текста:
groupText.append("textPath")
.attr("xlink:href", function(d, i) { return "#group" + i; })
.text(function(d, i) { return cities[i].name; })
.attr("startOffset",function(d,i) { return (groupPath[0][i].getTotalLength() - 48) / 4 })
.style("text-anchor","middle");
Я вычитаю 48, потому что стороны якоря имеют 24 пикселя каждая (разница в радиусах). Я делю на четыре, потому что путь удваивается сам по себе. Если бы это была общая линия, я бы просто разделил на два.
Этот подход немного упрощен, поскольку внешняя окружность не совпадает с внутренней окружностью каждого аккордового якоря, поэтому я немного отклонился, но он должен быть работоспособным.
Для надписей, которые находятся на острие отображения, это будет неудобно: внутренний радиус короче, поэтому формула для определения, является ли строка достаточно короткой для отображения, может быть неправильной - что может привести к тому, что некоторые символы будут подниматься вверх якоря (ваш пример также 16 пикселей как разница в радиусах для расчета, если текст слишком длинный, а не 24).
Это конечный результат:
Вот демонстрация.
groupText
.append("textPath")
.attr("xlink:href", function(d, i) { return "#group" + i; })
.text(function(d, i) { return cities[i].name; })
.attr("startOffset",function(d,i) { return (groupPath[0][i].getTotalLength() - 48) / 4 })
.style("text-anchor","middle");
Я вычитаю 48, потому что стороны якоря по 24 пикселя (разница в радиусах). Я делю на четыре, потому что путь возвращается сам к себе. Если бы это была общая линия, я бы просто разделил ее на два.
Этот подход немного упрощен, поскольку внешняя окружность не совпадает с внутренней окружностью каждого анкера хорды, поэтому я немного отклоняюсь, но это должно быть работоспособно.