我想生成多行的 d3 颜色表,在下面的代码中,看起来颜色表根本没有更新。
color_table()
function color_table() {
var categorical = [
{ "name" : "schemeAccent", "n": 8},
{ "name" : "schemeDark2", "n": 8},
{ "name" : "schemePastel2", "n": 8},
{ "name" : "schemeSet2", "n": 8},
{ "name" : "schemeSet1", "n": 9},
{ "name" : "schemePastel1", "n": 9},
{ "name" : "schemeCategory10", "n" : 10},
{ "name" : "schemeSet3", "n" : 12 },
{ "name" : "schemePaired", "n": 12},
//{ "name" : "schemeCategory20", "n" : 20 },
//{ "name" : "schemeCategory20b", "n" : 20},
//{ "name" : "schemeCategory20c", "n" : 20 }
]
var width = 400,
height = 500;
var colorScale = null
var n = 0,
unit = 0
var svg = d3.select('body').selectAll('cols')
.data(categorical)
.enter()
.append('svg')
.attr('width',width)
.attr('height',height/categorical.length)
.style('border','1px solid red')
var bars = svg.selectAll(".bars")
.data((d,i) => {
n = d.n
unit = width/n
//console.log(n)
colorScale = d3.scaleOrdinal(d3[d.name]);
return d3.range(n)})
.enter().append("rect")
.attr("class", "bars")
.attr("x", function(d, i) { return i * unit; })
.attr("y", 0)
.attr("height", height)
.attr("width", unit)
.style("fill", (d,i) => colorScale(d) )
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
答案 0 :(得分:3)
您的色阶完全相同,因为您为每个色阶引用了相同的变量:colorScale
。通过为每个数据评估 colorScale = d3.scaleOrdinal(d3[d.name]);
,然后才添加矩形,它们根据最后处理的数据着色以定义 colorScale
。这也是每个系列的矩形宽度相同的原因。
为了解决这个问题,我们可以使用 d3.local,它似乎是为这种情况而设计的:
<块引用>D3 locals 允许您定义独立于数据的本地状态。为了 例如,当渲染时间序列数据的小倍数时,您 可能希望所有图表使用相同的 x 尺度但不同的 y 尺度 比较每个指标的相对性能。 (docs)
使用 d3 local 我们可以保存每个系列的不同颜色比例和宽度值:
var localScale = d3.local();
var localWidth = d3.local();
我们可以使用 local.set
为每个父节点设置它们的值,如下所示,它为指定节点设置一个值(分别为第二个和第一个参数):
var svg = d3.select('body').selectAll('cols')
.data(categorical)
.enter()
.append('svg')
.each(function(d) {
localScale.set(this,d3.scaleOrdinal(d3[d.name]));
localWidth.set(this,width/d.n);
})
要获取值,我们可以使用 local.get(node)
,作为子节点。虽然我们还没有为子节点定义局部值,但 local.get
会找到最近的具有定义值的祖先,因此我们可以将矩形提供给 local.get
:
var bars = svg.selectAll(".bars")
.data(d => d3.range(d.n))
.enter().append("rect")
.attr("x", function(d, i) {
return i * localWidth.get(this);
})
.attr("width", function() {
return localWidth.get(this)
})
.style("fill", function(d) {
return localScale.get(this)(d) ;
})
...
我已将传递的数据更改为简单的 d3.range(d.n)
,因为我们不再需要在这里计算任何其他内容。
总的来说,这给了我们:
color_table()
function color_table() {
var categorical = [
{ "name" : "schemeAccent", "n": 8},
{ "name" : "schemeDark2", "n": 8},
{ "name" : "schemePastel2", "n": 8},
{ "name" : "schemeSet2", "n": 8},
{ "name" : "schemeSet1", "n": 9},
{ "name" : "schemePastel1", "n": 9},
{ "name" : "schemeCategory10", "n" : 10},
{ "name" : "schemeSet3", "n" : 12 },
{ "name" : "schemePaired", "n": 12},
]
var width = 400,
height = 500;
var localScale = d3.local();
var localWidth = d3.local();
var n = 0,
unit = 0
var svg = d3.select('body').selectAll('cols')
.data(categorical)
.enter()
.append('svg')
.attr('width',width)
.attr('height',height/categorical.length)
.style('border','1px solid red')
.each(function(d) {
localScale.set(this,d3.scaleOrdinal(d3[d.name]));
localWidth.set(this,width/d.n);
})
var bars = svg.selectAll(".bars")
.data(d => d3.range(d.n))
.enter().append("rect")
.attr("class", "bars")
.attr("x", function(d, i) { return i * localWidth.get(this); })
.attr("y", 0)
.attr("height", height)
.attr("width", function() {
return localWidth.get(this)
})
.style("fill", function(d) {
return localScale.get(this)(d) ;
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
提供:
另一种方法是将比例尺存储在基准本身上,无论是父级还是子级,但 d3.local 就是为此目的而设计的,并且可能是最简单的解决方案。