我有一个如下所示的圆圈:
您可能会注意到,与其他标签相比,从 0 到 9 的标签与外圆的距离不同。我的期望是:
- 如何使标签到外圆的距离相同?
- 如何排列标签,使其位于中心线的中间,而不是侧面?
您可以在这里找到我的代码:https://jsfiddle.net/ao4xwpnk/
我尝试更改这部分代码以使标签对称,但没有成功。
let featureData = features.map((f, i) => {
let angle = (Math.PI / 2) + (2 * Math.PI * i / features.length);
let isLeftHalf = angle < Math.PI; // Check if the angle is in the left half of the circle
let value = isLeftHalf ? 14.5 : 10.5; // Adjust the value for labels in left and right halves
return {
"name": f,
"angle": angle,
"line_coord": angleToCoordinate(angle, 180),
"label_coord": angleToCoordinate(angle, value)
};
});
最佳答案
使用text-anchor
和 dy
text
元素上的属性将有所帮助:
// draw axis label
svg.selectAll(".axislabel")
.data(featureData)
.join(
enter => enter.append("text")
.attr("x", d => d.label_coord.x)
.attr("y", d => d.label_coord.y)
.attr("text-anchor", "middle") // <-- here
.attr("dy", "0.35em") // <-- and here
.text(d => d.name)
text-anchor
适用于水平放置:
The rendered characters are aligned such that the middle of the text string is at the current text position.
这可以解决您尝试使用 isLeftHalf
的问题。
dy
属性适用于垂直放置。 0.35em
的值是一个约定 - 请参阅此 answer有趣的是:
Using dy=0.35em can help vertically centre text regardless of font size
我对此的理解是,该值占字体设计中下降部分的平均值,例如对于 p 和 g,“尾部”大约是高度的 35%,因此您将标签“向下”推,使其与轴对齐。请参阅here例如。
此更新后的代码:
let data = [];
let features = [0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; // labels on the circle
//generate the data
for (var i = 0; i < 2; i++) {
var point = {}
//each feature will be a random number from 1-9
features.forEach(f => point[f] = 1 + Math.random() * 162);
data.push(point);
}
let width = 600;
let height = 600;
let svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//adding radial scale
let radialScale = d3.scaleLinear()
.domain([0, 180])
.range([0, 250]);
//adding ticks
let ticks = [20, 40, 60, 80, 100, 120, 140, 160, 180]; // 9 circles
//adding circles
svg.selectAll("circle")
.data(ticks)
.join(
enter => enter.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("fill", "none")
.attr("stroke", "gray")
.attr("r", d => radialScale(d))
.attr("stroke-width", 1) // Set the same stroke-width for all circles
);
//adding white background circle to remove filling in the center
svg.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("fill", "white")
.attr("stroke", "gray")
.attr("r", radialScale(20))
.attr("stroke-width", 1); // Set the same stroke-width for the center circle
//adding text labels
svg.selectAll(".ticklabel")
.data(ticks)
.join(
enter => enter.append("text")
.attr("class", "ticklabel")
.attr("x", width / 2 + 5)
.attr("y", d => height / 2 - radialScale(d))
.text(d => d.toString())
);
//Plotting the Axes
//map an angle and value into SVG
function angleToCoordinate(angle, value) {
let x = Math.cos(angle) * radialScale(value);
let y = Math.sin(angle) * radialScale(value);
return { "x": width / 2 + x, "y": height / 2 - y };
}
let featureData = features.map((f, i) => {
let angle = (Math.PI / 2) + (2 * Math.PI * i / features.length);
let value = i < 9 ? 10.5 : 12; // Adjust the value for labels 13 to 21 to 12
return {
"name": f,
"angle": angle,
"line_coord": angleToCoordinate(angle, 180),
"label_coord": angleToCoordinate(angle, 190) // changin 190 allows me to adjust labels proximity to the circle!
};
});
// draw axis line
svg.selectAll("line")
.data(featureData)
.join(
enter => enter.append("line")
.attr("x1", width / 2)
.attr("y1", height / 2)
.attr("x2", d => d.line_coord.x)
.attr("y2", d => d.line_coord.y)
.attr("stroke", "black")
.attr("stroke-width", 1) // Set the same stroke-width for all axis lines
);
// draw axis label
svg.selectAll(".axislabel")
.data(featureData)
.join(
enter => enter.append("text")
.attr("x", d => d.label_coord.x)
.attr("y", d => d.label_coord.y)
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.text(d => d.name)
);
//draw shapes for actual data
let line = d3.line()
.x(d => d.x)
.y(d => d.y);
let area = d3.area()
.x(d => d.x)
.y0(d => d.y)
.y1(height / 2)
.curve(d3.curveLinear);
let colors = ["red", "green", "navy"];
//helper function to iterate through fields in each data point
function getPathCoordinates(data_point) {
let coordinates = [];
for (var i = 0; i < features.length; i++) {
let ft_name = features[i];
let angle = (Math.PI / 2) + (2 * Math.PI * i / features.length);
coordinates.push({
x: width / 2 + Math.cos(angle) * radialScale(data_point[ft_name]),
y: height / 2 - Math.sin(angle) * radialScale(data_point[ft_name])
});
}
return coordinates;
}
// Modify the path elements to separate red and green data
svg.selectAll(".path-red")
.data([data[0]]) // Data for the red line
.join(
enter => enter.append("path")
.datum(d => getPathCoordinates(d))
.attr("class", "path-red")
.attr("d", line)
.attr("stroke-width", 3)
.attr("stroke", "red")
.attr("fill", "none") // No fill for the red line
);
svg.selectAll(".path-green")
.data([data[1]]) // Data for the green line
.join(
enter => enter.append("path")
.datum(d => getPathCoordinates(d))
.attr("class", "path-green")
.attr("d", line)
.attr("stroke-width", 3)
.attr("stroke", "green")
.attr("fill", "none") // No fill for the green line
);
// Fill the area between the red and green lines
let areaFillData = [
...getPathCoordinates(data[0]),
...getPathCoordinates(data[1]).reverse()
];
svg.append("path")
.datum(areaFillData)
.attr("d", area)
.attr("stroke-width", 0)
.attr("fill", "blue") // Fill the area between the red and green lines with blue color
.attr("fill-opacity", 0.3); // You can adjust the opacity as you like
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.0/d3.min.js"></script>
关于javascript - 如何正确排列圆的标签?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76603658/