d3.js - 从数组生成颜色 table
d3.js - generate color table from array
我想生成多行的 d3 颜色 table,在下面的代码中,看起来颜色 table 根本没有更新。
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>
你的色标都是一样的,因为你为每个色标引用了相同的变量:colorScale
。通过为每个数据评估 colorScale = d3.scaleOrdinal(d3[d.name]);
并且仅在之后添加矩形,它们根据最后处理的数据着色以定义 colorScale
。这也是为什么每个系列都有相同的矩形宽度。
要解决这个问题,我们可以使用 d3.local,它似乎是为这种情况设计的:
D3 locals allow you to define local state independent of data. For
instance, when rendering small multiples of time-series data, you
might want the same x-scale for all charts but distinct y-scales to
compare the relative performance of each metric. (docs)
使用 d3 local 我们可以为每个系列保存不同的色标和宽度值:
var localScale = d3.local();
var localWidth = d3.local();
并且我们可以使用 local.set
为每个父节点设置它们的值,如下所示,它为指定节点设置一个值(分别为第 2 个和第 1 个参数):
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 是为此目的而设计的,可能是最简单的解决方案。
我想生成多行的 d3 颜色 table,在下面的代码中,看起来颜色 table 根本没有更新。
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>
你的色标都是一样的,因为你为每个色标引用了相同的变量:colorScale
。通过为每个数据评估 colorScale = d3.scaleOrdinal(d3[d.name]);
并且仅在之后添加矩形,它们根据最后处理的数据着色以定义 colorScale
。这也是为什么每个系列都有相同的矩形宽度。
要解决这个问题,我们可以使用 d3.local,它似乎是为这种情况设计的:
D3 locals allow you to define local state independent of data. For instance, when rendering small multiples of time-series data, you might want the same x-scale for all charts but distinct y-scales to compare the relative performance of each metric. (docs)
使用 d3 local 我们可以为每个系列保存不同的色标和宽度值:
var localScale = d3.local();
var localWidth = d3.local();
并且我们可以使用 local.set
为每个父节点设置它们的值,如下所示,它为指定节点设置一个值(分别为第 2 个和第 1 个参数):
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 是为此目的而设计的,可能是最简单的解决方案。