javascript 来自字符串离散域的连续色标?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12217121/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 15:37:15  来源:igfitidea点击:

Continuous color scale from discrete domain of strings?

javascriptd3.jsheatmap

提问by amergin

I'm implementing a heatmap in which the cell background color is determined by a d3 color scale. Some of the values are categorical; their value can be of N different arbitrary string-type categories like ["6TH", "7TH", "5TH", "4TH"].

我正在实施一个热图,其中单元格背景颜色由 d3 色标确定。有些值是分类的;它们的值可以是 N 个不同的任意字符串类型类别,如 ["6TH", "7TH", "5TH", "4TH"]。

Given a start color d3.rgb("blue") and an end color d3.rgb("red"), how can I construct color scale that maps a discrete domain of strings into a continuous color range?

给定起始颜色 d3.rgb("blue") 和结束颜色 d3.rgb("red"),如何构建将离散的字符串域映射到连续颜色范围的色标?

I tried

我试过

var scale = d3.scale.ordinal()
    .domain(["6TH", "7TH", "5TH", "4TH"])
    .rangeBands( [ d3.rgb("blue"), d3.rgb("red") ] );

which obviously doesn't work.

这显然不起作用。

回答by mbostock

First, I would consider using one of the readily-available Colorbrewer scales; see colorbrewer2.org. These are also available as JavaScript and CSS files in D3's git repository; see lib/colorbrewer. For example, if you have four discrete values in your domain, and you want a red-blue diverging scale, you could say:

首先,我会考虑使用现成的 Colorbrewer 量表之一;见colorbrewer2.org。这些也可以在 D3 的 git 存储库中作为 JavaScript 和 CSS 文件使用;见lib/colorbrewer。例如,如果您的域中有四个离散值,并且您想要一个红蓝发散尺度,您可以说:

var color = d3.scale.ordinal()
    .domain(["6TH", "7TH", "5TH", "4TH"])
    .range(colorbrewer.RdBu[4]);

(You'll need a <script src="colorbrewer.js"></script>somewhere before this, too.) Colorbrewer has a variety of well-designed sequential, diverging and categorical color scales.

<script src="colorbrewer.js"></script>在此之前您也需要一个。)Colorbrewer 有各种精心设计的顺序、发散和分类色标。

If you insist on rolling your own color scale, I strongly recommend interpolating in L*a*b* or HCL color spacefor accurate perception. You can do this using d3.interpolateLabor d3.interpolateHcl. For example, d3.interpolateLab("red", "blue")(.5)returns a color halfway between red and blue.

如果你坚持滚动你自己的色标,我强烈建议在L*a*b* 或 HCL 颜色空间中进行插值以获得准确的感知。您可以使用d3.interpolateLabd3.interpolateHcl执行此操作。例如,d3.interpolateLab("red", "blue")(.5)返回介于红色和蓝色之间的颜色。

To compute the colors for your ordinal scale's range, you can use an interpolator, or you might find a temporary linear scale more convenient. For example:

要计算序数比例范围的颜色,您可以使用插值器,或者您可能会发现临时线性比例更方便。例如:

var categories = ["6TH", "7TH", "5TH", "4TH"];

var color = d3.scale.ordinal()
    .domain(categories)
    .range(d3.range(categories.length).map(d3.scale.linear()
      .domain([0, categories.length - 1])
      .range(["red", "blue"])
      .interpolate(d3.interpolateLab)));

回答by Justin Ethier

You have the right idea, you just need to process each R/G/B color channel. For example, in vanilla JavaScript you can do the following:

你有正确的想法,你只需要处理每个 R/G/B 颜色通道。例如,在 vanilla JavaScript 中,您可以执行以下操作:

var a = [255, 0, 0], // First color
    b = [0, 0, 255], // Other color
    bands = 5,       // Bands is the length of your domain
    i, 
    delta = [];      // Difference between color in each channel

// Compute difference between each color
for (i = 0; i < 4; i++){
  delta[i] = (a[i] - b[i]) / (bands + 1);
}

// Use that difference to create your bands
for (i = 0; i <= bands + 1; i++){
  var r = Math.round(a[0] - delta[0] * i);
  var g = Math.round(a[1] - delta[1] * i);
  var b = Math.round(a[2] - delta[2] * i);
  console.log("<div style='background-color: #" + dec2hex(r) + dec2hex(g) + dec2hex(b) + "'>Band " + i + "</div>");
}

// A helper function for formatting
function dec2hex(i) {
  return (i+0x100).toString(16).substr(-2).toUpperCase();
}

According to the d3 documentation, you can extract each color channel using the r, gand battributes of a color object:

根据d3 文档,您可以使用颜色对象的r,gb属性提取每个颜色通道:

# d3.rgb(color)

Constructs a new RGB color by parsing the specified color string. If color is not a string, it is coerced to a string; thus, this constructor can also be used to create a copy of an existing color, or force the conversion of a d3.hsl color to RGB.

...

The resulting color is stored as red, green and blue integer channel values in the range [0,255]. The channels are available as the r, g and b attributes of the returned object.

# d3.rgb(color)

通过解析指定的颜色字符串构造新的 RGB 颜色。如果颜色不是字符串,则强制为字符串;因此,此构造函数还可用于创建现有颜色的副本,或强制将 d3.hsl 颜色转换为 RGB。

...

生成的颜色存储为 [0,255] 范围内的红色、绿色和蓝色整数通道值。通道可用作返回对象的 r、g 和 b 属性。

So at the top of the example above, you could say:

所以在上面例子的顶部,你可以说:

var myColor = d3.rgb("blue"),
    a = [myColor.r, myColor.g, myColor.b],
...

Does that help?

这有帮助吗?

回答by óscar Gómez Alca?iz

You can always chain an ordinal scale and a linear scale.

您始终可以链接顺序刻度和线性刻度。

The first scale will create quantifiable values from your discrete values, and the second scale will interpolate these values on a color scale.

第一个比例将从您的离散值创建可量化的值,第二个比例将在色标上插入这些值。

Something like this:

像这样的东西:

// Your categories
var data = ["6TH", "7TH", "5TH", "4TH"],

  // Define ordinal to linear scale...
  ordinal = d3.scale.ordinal().domain(data).rangePoints([0, 1]),

  // ...and linear to color scale
  linear = d3.scale.linear().domain([0, 1]).range([d3.rgb("blue"), d3.rgb("red")]);

// Now define your artificial 'compound' scale
function scale(d) {
  return linear(ordinal(d));
}

// And then use it on your D3 code
d3.selectAll('div')
  .data(data)
  .enter()
  .append('div')
  .style('background', scale) // <- et voilà ;)
  .text(function(d) {
    return d;
  });
div {
  color: white;
  width: 3em;
  padding: 1em;
  margin: .2em
  text-align: center;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: bold
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>