Я использую пример диаграммы аккордов d3 от Andrew и хочу сосредоточить все текстовые метки внутри изогнутого фрагмента. Я много пробовал, но никогда не мог сосредоточить тексты. Знаете ли вы, что такое уловка 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>
В стороне, я бы предложил посмотреть обновление до версии 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).
Это конечный результат:
Вот демонстрация.