在D3饼图中为特定分段分配特定颜色

3

我使用D3js (v4)作为Ember组件构建了一个饼图/甜甜圈图,并尝试使用特定标签的段填充特定颜色,但这证明是困难的。

以下是用于着色的代码:

marc = arc().outerRadius(radius - 10).innerRadius(radius - donutwidth),
color = scaleOrdinal().range(['#49b6d6', '#f59c1a', '#ff5b57',  '#00acac',]),

gEnter.append("path")
      .attr("d", marc)
      .attr("fill", (d, i) => {
        return color(i);
      })

以上代码可以正常运行并使用选定的颜色填充弧形,但不能使用我想要的每个弧形的颜色。数组的索引是一致的,所以我尝试通过重新排列颜色的顺序来解决问题,但没有效果。

我还尝试了使用基于索引的 if 语句:

gEnter.append("path")
          .attr("d", marc)
          .attr("fill", (d, i) => {
            if (i === 0 { return color([0]) }
       })

这确实填充了索引为0的段落,但没有使用列表中选择的颜色。更改color([0])中的数字实际上根本没有任何变化。如果我尝试使用基于Label字符串而不是数组索引的条件语句,情况也是如此。

编辑

作为Ember Computed Property的一部分,用于为图表格式化数据,数据被重新排序,以便每个标签每次呈现时都按相同的顺序呈现。计算属性如下所示:

//takes the ember model 'referralsource' and groups it as needed   
sourceData: groupBy('referralsource', 'label'),

//ember computed property that provides data to chart
  pieData: Ember.computed('sourceData', function() {

    let objs = this.get('sourceData')
    let sortedObjs = _.sortBy(objs, 'value')
    let newArray = []

    sortedObjs.forEach(function(x) {
      let newLabel = x.value
      let count = x.items.length
      let newData = {
        label: newLabel,
        count: count
      }
      newArray.push(newData)
    })
    return newArray
  }),

在创建您的甜甜圈时,您是否对任何内容进行排序或重新排序 - 如果您登录选择填充函数,则每个数据是否对应正确的索引? - Andrew Reid
嗨,安德鲁,我已经编辑了上面的问题。我对对象进行排序是为了尝试以一致的顺序列出它们,因为先前它们可以以任何顺序返回,我认为这会导致每个段的颜色发生变化的问题。当我记录(i,d)时,我可以看到(i)的值并不总是匹配单个对象中的索引值。实际上,唯一匹配的值是在我遵循Thiago的建议添加.domain时着色的唯一值。 - Sorvah
2个回答

1
在你的第一个例子中,尝试更改这个:

color = scaleOrdinal().range(['#49b6d6', '#f59c1a', '#ff5b57',       '#00acac',]),

对于这个:
color = scaleOrdinal().range([0,4]),
color.domain(['#49b6d6', '#f59c1a', '#ff5b57', '#00acac']),

你使用的范围用于指示比例尺的大小(在这种情况下为4,因为你放置了4种颜色),而域则指定该比例尺每个位置上的内容。

我明白你的意思。我把这一行改成了 color = scaleOrdinal().range([0, 4]).domain(['#f59c1a','#49b6d6', '#ff5b57', '#00acac']),但是这实际上停止了对弧形的着色。它们仍然绘制出来,但都是黑色的。我需要改变其他什么吗? - Sorvah
你是在使用d3.scaleOrdinal...还是只用scaleOrdinal? - Thiago Outeiro Pereira Damasce
这是一个Ember组件,因此D3作为模块导入,而不是通过d3.调用,感谢ember-d3 - Sorvah

1
如果您的标签每次都相同(或来自相同的选项池),则可以指定特定域。在序数比例尺中,该域设置为指定的值数组。域中的第一个元素将映射到范围中的第一个元素,第二个域值将映射到第二个范围值,依此类推(来自API文档)。通过将域设置为包含每个可能标签选项的数组,您可以轻松地为每个标签分配一种颜色。下面的示例有五个可能的标签,第一行使用与第二行相反的数据数组顺序,第三行使用具有重复项的随机顺序。所有三行都将每个数据与特定颜色关联起来。

var labels = ["redData","blueData","orangeData","pinkData","greenData"];

var colors = ["crimson","steelblue","orange","lightsalmon","lawngreen"];

var scale = d3.scaleOrdinal()
  .domain(labels)  // input values
  .range(colors);  // output values
  
var svg = d3.select("svg");

// initial order
svg.selectAll(null)
  .data(labels)
  .enter()
  .append("circle")
  .attr("cy",40)
  .attr("cx", function(d,i) { return i * 40+ 20; })
  .attr("r",15)
  .attr("fill",function(d) { return scale(d); });
  
// reverse order
svg.selectAll(null)
  .data(labels.reverse())
  .enter()
  .append("circle")
  .attr("cy",80)
  .attr("cx", function(d,i) { return i * 40+ 20; })
  .attr("r",15)
  .attr("fill",function(d) { return scale(d); });

// random labels
svg.selectAll(null)
  .data(["blueData","blueData","redData","orangeData","blueData"])
  .enter()
  .append("circle")
  .attr("cy",120)
  .attr("cx", function(d,i) { return i * 40+ 20; })
  .attr("r",15)
  .attr("fill",function(d) { return scale(d); });
  
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


<svg width="600" height="400"></svg>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接