d3.js - 从数组生成颜色表

时间:2021-06-08 19:09:37

标签: d3.js

我想生成多行的 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>

1 个答案:

答案 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>

提供:

enter image description here

另一种方法是将比例尺存储在基准本身上,无论是父级还是子级,但 d3.local 就是为此目的而设计的,并且可能是最简单的解决方案。

相关问题