如何使d3路径描边向内弯曲?就像 "cardinal-closed" 插值的反向
How to make d3 path stroke to curve inside? Like the reverse of "cardinal-closed" interpolation
我正在使用 d3 开发一个新项目,创建一个显示从 0 到 10 的分数的图表。数据如下所示:
var data = [
{axis: 'People', value: getRandomScore()},
{axis: 'Leadership', value: getRandomScore()},
{axis: 'Resources', value: getRandomScore()},
{axis: 'Processes', value: getRandomScore()},
{axis: 'Strategy', value: getRandomScore()},
{axis: 'Culture', value: getRandomScore()},
];
getRandomScore 是一种 returns 从 0 到 10 的随机整数的方法。
这是我设法实现的结果:
这是艺术品:
问题是 d3 没有选项可以让路径描边(通过点)向内弯曲,使路径看起来像艺术作品中的星形,我需要一些帮助来创建一个函数会在曲线内做那件事。
你没有具体说明你是如何绘制的,所以在我的回答中我会假设它是使用 d3.svg.line.radial
的极坐标图的变体。获得所需外观的最简单方法是在现有点之间插入 "fake" 点以强制向内插值。
假设你的点位于圆极轴上(x 以弧度为单位):
var data = [
[Math.PI / 3, someValue],
[0 * Math.PI, someValue],
[(5 * Math.PI / 3), someValue],
[(4 * Math.PI / 3), someValue],
[Math.PI, someValue],
[(2 * Math.PI) / 3, someValue]
];
然后使用这些弧度的中点:
var midPoints = [Math.PI / 6, (11 * Math.PI) / 6,
(3 * Math.PI) / 2, (7 * Math.PI) / 6,
(5 * Math.PI) / 6, Math.PI / 2];
pD = [];
midPoints.forEach(function(d,i){
var i2 = (i === 5) ? 0 : i + 1;
var midY = d3.min([data[i][1],data[i2][1]]) / 2; // find the min of the two neighboring points and the "inner" fake to half the min
pD.push(data[i]);
pD.push([d, midY]);
});
我将我的内部假点设置为两个相邻点的最小值的一半。您可以调整它以获得您想要的效果。
完整的工作代码如下:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.frame {
fill: none;
stroke: #000;
}
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis circle {
fill: none;
stroke: steelblue;
stroke-dasharray: 4;
}
.axis:last-of-type circle {
stroke: steelblue;
stroke-dasharray: none;
}
.line {
fill: none;
stroke: orange;
stroke-width: 3px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 500,
height = 500,
radius = Math.min(width, height) / 2 - 30;
var r = d3.scale.linear()
.domain([0, 2])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(3).slice(1))
.enter().append("g");
gr.append("circle")
.attr("r", r);
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 60))
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + -d + ")";
});
ga.append("line")
.attr("x2", radius);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
})
.interpolate("cardinal-closed");
var data = [
[Math.PI / 3, Math.random() + 1],
[0 * Math.PI, Math.random() + 1],
[(5 * Math.PI / 3), Math.random() + 1],
[(4 * Math.PI / 3), Math.random() + 1],
[Math.PI, Math.random() + 1],
[(2 * Math.PI) / 3, Math.random() + 1]
]
var midPoints = [Math.PI / 6, (11 * Math.PI) / 6,
(3 * Math.PI) / 2, (7 * Math.PI) / 6,
(5 * Math.PI) / 6, Math.PI / 2];
pD = [];
midPoints.forEach(function(d,i){
var i2 = (i === 5) ? 0 : i + 1;
var midY = d3.min([data[i][1],data[i2][1]]) / 2;
pD.push(data[i]);
pD.push([d, midY]);
});
svg.selectAll("point")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
})
.attr("r", 8)
.attr("fill", "steelblue");
svg.append("path")
.datum(pD)
.attr("class", "line")
.attr("d", line);
</script>
我正在使用 d3 开发一个新项目,创建一个显示从 0 到 10 的分数的图表。数据如下所示:
var data = [
{axis: 'People', value: getRandomScore()},
{axis: 'Leadership', value: getRandomScore()},
{axis: 'Resources', value: getRandomScore()},
{axis: 'Processes', value: getRandomScore()},
{axis: 'Strategy', value: getRandomScore()},
{axis: 'Culture', value: getRandomScore()},
];
getRandomScore 是一种 returns 从 0 到 10 的随机整数的方法。
这是我设法实现的结果:
这是艺术品:
问题是 d3 没有选项可以让路径描边(通过点)向内弯曲,使路径看起来像艺术作品中的星形,我需要一些帮助来创建一个函数会在曲线内做那件事。
你没有具体说明你是如何绘制的,所以在我的回答中我会假设它是使用 d3.svg.line.radial
的极坐标图的变体。获得所需外观的最简单方法是在现有点之间插入 "fake" 点以强制向内插值。
假设你的点位于圆极轴上(x 以弧度为单位):
var data = [
[Math.PI / 3, someValue],
[0 * Math.PI, someValue],
[(5 * Math.PI / 3), someValue],
[(4 * Math.PI / 3), someValue],
[Math.PI, someValue],
[(2 * Math.PI) / 3, someValue]
];
然后使用这些弧度的中点:
var midPoints = [Math.PI / 6, (11 * Math.PI) / 6,
(3 * Math.PI) / 2, (7 * Math.PI) / 6,
(5 * Math.PI) / 6, Math.PI / 2];
pD = [];
midPoints.forEach(function(d,i){
var i2 = (i === 5) ? 0 : i + 1;
var midY = d3.min([data[i][1],data[i2][1]]) / 2; // find the min of the two neighboring points and the "inner" fake to half the min
pD.push(data[i]);
pD.push([d, midY]);
});
我将我的内部假点设置为两个相邻点的最小值的一半。您可以调整它以获得您想要的效果。
完整的工作代码如下:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.frame {
fill: none;
stroke: #000;
}
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis circle {
fill: none;
stroke: steelblue;
stroke-dasharray: 4;
}
.axis:last-of-type circle {
stroke: steelblue;
stroke-dasharray: none;
}
.line {
fill: none;
stroke: orange;
stroke-width: 3px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 500,
height = 500,
radius = Math.min(width, height) / 2 - 30;
var r = d3.scale.linear()
.domain([0, 2])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(3).slice(1))
.enter().append("g");
gr.append("circle")
.attr("r", r);
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 60))
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + -d + ")";
});
ga.append("line")
.attr("x2", radius);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
})
.interpolate("cardinal-closed");
var data = [
[Math.PI / 3, Math.random() + 1],
[0 * Math.PI, Math.random() + 1],
[(5 * Math.PI / 3), Math.random() + 1],
[(4 * Math.PI / 3), Math.random() + 1],
[Math.PI, Math.random() + 1],
[(2 * Math.PI) / 3, Math.random() + 1]
]
var midPoints = [Math.PI / 6, (11 * Math.PI) / 6,
(3 * Math.PI) / 2, (7 * Math.PI) / 6,
(5 * Math.PI) / 6, Math.PI / 2];
pD = [];
midPoints.forEach(function(d,i){
var i2 = (i === 5) ? 0 : i + 1;
var midY = d3.min([data[i][1],data[i2][1]]) / 2;
pD.push(data[i]);
pD.push([d, midY]);
});
svg.selectAll("point")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
})
.attr("r", 8)
.attr("fill", "steelblue");
svg.append("path")
.datum(pD)
.attr("class", "line")
.attr("d", line);
</script>