请参阅 Observablehq 笔记本中的代码:https ://observablehq.com/@erayalan/radial-scatter-plot
我试图了解如何更改这件作品中每个项目的大小和颜色:
data = [{ name : "foo", r: 40, theta: 1.4 },
{ name : "bar", r: 30, theta: 4.8 },
{ name : "baz", r: 60, theta: 3.05}]
请参阅 Observablehq 笔记本中的代码:https ://observablehq.com/@erayalan/radial-scatter-plot
我试图了解如何更改这件作品中每个项目的大小和颜色:
data = [{ name : "foo", r: 40, theta: 1.4 },
{ name : "bar", r: 30, theta: 4.8 },
{ name : "baz", r: 60, theta: 3.05}]
要更改圆圈的大小,您可以使用该"r"
属性,类似于使用“cx”和“cy”的方式。我假设您想将“theta”映射到大小。首先,您需要创建一个比例:
const radiusScale = d3.scaleLinear().domain(d3.extent(data, d=> d.theta)).range([10, 20])
函数extent ind3.extent(data, d=> d.theta)
返回 theta 属性中数据的最大值和最小值(在本例中为[1.4, 4.8]
)。这意味着这个尺度有一个域[1.4, 4.8]
和一个范围[10, 20]
,所以如果你调用radiusScale(1.4)
它就会输出10
,并且radiusScale(4.8)
会输出20
。有了这个,您将 theta 值映射到足够的半径,并可以将其用于"r"
SVG 圆的属性:
.attr("cx", d => d.r * Math.cos(d.theta))
.attr("cy", d => d.r * Math.sin(d.theta))
.attr("r", d => radiusScale(d.theta))
类似的逻辑适用于在"fill"
属性中定义的颜色。您想将“foo”、“bar”和“baz”映射到不同的颜色。您可以scaleOrdinal
为此使用和颜色数组:
const colorScale = d3.scaleOrdinal().range(d3.schemeCategory10)
这个比例的范围是d3.schemeCategory10
,它只是一个包含一些常见颜色的数组(您可以使用任何颜色数组)。您不需要此比例的域,因为 d3 可以自动为比例接收的每个唯一值分配不同的颜色。
然后您可以补充映射,将颜色分配给填充属性。
.attr("cx", d => d.r * Math.cos(d.theta))
.attr("cy", d => d.r * Math.sin(d.theta))
.attr("r", d => radiusScale(d.theta))
.attr("fill", d => colorScale(d.name))
这是您的笔记本的一个分支,其中包含上述更改:
在我的回答中,我将 theta 的值直接映射到了半径。不推荐使用圆的半径来编码一个值,最好的做法是使用它的面积。为了简单起见,我不会在这里深入探讨,但您可以在此处查看更好的映射大小方法。
D3 的学习曲线可能有点陡峭。您在此答案中看到了几个更改颜色和大小的概念(比例、域、范围、范围等!)。您可能需要查看官方 d3 介绍,以更顺序的方式查看这些概念。
这是一个包含大小和颜色的片段:
data = [
{name: "foo", r: 40, theta: 1.4, size: 10, color: 'red'},
{name: "bar", r: 30, theta: 4.8, size: 12, color: 'green'},
{name: "baz", r: 60, theta: 3.05, size: 14, color: 'blue'}
];
const width = 200;
const height = 200;
const svg = d3.select('svg');
const radius = 100;
const r = d3.scaleLinear()
.domain([0, 1])
.range([0, radius]);
svg
.attr("viewBox", `${-width / 2} ${-height / 2} ${width} ${height}`)
.attr('width', width)
.attr('height', height)
.style("font", "10px sans-serif");
const gr = svg.append('g')
.attr('class', 'r axis')
.selectAll('g')
.data(r.ticks(5).slice(1))
.enter()
.append('g');
gr.append('circle')
.attr("fill", "none")
.attr("stroke", "#ebebeb")
.attr('r', r);
const ga = svg.append('g')
.attr('class', 'a axis')
.selectAll('g')
.data(d3.range(0, 360, 30)) // line density
.enter()
.append('g')
.attr('transform', d => 'rotate(' + -d + ')');
ga.append('line')
.attr("fill", "none")
.attr("stroke", "#aaa")
.attr('x2', radius);
svg.selectAll("point") // No longer "rect"
.data(data)
.enter()
.append("circle")
.attr("cx", d => d.r * Math.cos(d.theta))
.attr("cy", d => d.r * Math.sin(d.theta))
.attr("r", d => d.size)
.style('stroke', d => d.color)
.style('fill', 'white');
svg.selectAll("text") // Note "text", not "circle" or "rect"
.data(data)
.enter()
.append("text")
.text(d => d.name)
.attr("x", d => d.r * Math.cos(d.theta))
.attr("y", d => d.r * Math.sin(d.theta))
.attr('alignment-baseline', 'middle')
.attr('text-anchor', 'middle')
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", d => d.color);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg/>