Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate 16 colors based on values from 3 colors

I'm trying to write function i n javascript, which can generate colors between three colors based on a given value.

Heres an explanation:


There are two colors : #0a0000, #ffaa03 & #ffffd5
There are total 16 sensor e.g (0.2,0.9,2,2.3,3.5,4,7.7 … 27.7 ) - this can be unordered
Now I am trying to find the min and the max from these sensor values, the lowest value would get #ffffd5 color, the highest value would get #0a0000 color, and the middle value will have #ffaa03 color. Now all the values in the middle will get the color between these two colors that are also close to the middle range color based on their value of how close they are to the minimum and maximum.

UPDATE

I have tried this below method but its giving wrong color values

let hex = '0123456789ABCDEF';

    let middleValue = this.maxValue / 2;
    if (x == this.minValue) {
      return '#ffffd5';
    } else if (x > middleValue - 1 && x < middleValue + 1) {
      return '#ffaa03';
    } else if (x == this.maxValue) {
      return '#0a0000';
    } else {
      if (x < middleValue - 1) {
        let perc = x * (middleValue - 1);
        let hexVal = hex[Math.round(16 * perc)];
        return '#FF' + hexVal + hexVal + '00';
      } else if (x > middleValue + 1) {
        let perc = x / (middleValue + 1) / 0.5 - 1;
        let hexVal = hex[hex.length - Math.round(16 * perc)];
        return '#' + hexVal + hexVal + '0000';
      }
    }

This is the image generated

enter image description here

Where as I want it to look similar to this depending on the values

enter image description here

like image 578
Maliha Khizer Avatar asked Sep 06 '25 01:09

Maliha Khizer


1 Answers

It seems to me that the scale you need is a simple linear scale, with the domain as the extent of your data and the range as ["#ffffd5", "#0a0000"].

Here is an example, with the data unsorted (as you mentioned):

const fakeData = d3.range(16).map(_ => d3.randomUniform(30)());
const scale = d3.scaleLinear()
  .domain(d3.extent(fakeData))
  .range(["#ffffd5", "#0a0000"]);
d3.select("body").selectAll(null)
  .data(fakeData)
  .enter()
  .append("div")
  .style("background-color", d => scale(d))
div {
  display: inline-block;
  margin-right: 4px;
  width: 20px;
  height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

And here with the data sorted:

const fakeData = d3.range(16).map(_ => d3.randomUniform(30)()).sort((a, b) => a - b);
const scale = d3.scaleLinear()
  .domain(d3.extent(fakeData))
  .range(["#ffffd5", "#0a0000"]);
d3.select("body").selectAll(null)
  .data(fakeData)
  .enter()
  .append("div")
  .style("background-color", d => scale(d))
div {
  display: inline-block;
  margin-right: 4px;
  width: 20px;
  height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Finally, the image you posted doesn't match the colours you described (from #ffffd5 to #0a0000). That said, you could alternatively use a sequential scale with another colour array, for instance:

const fakeData = d3.range(16).map(_ => d3.randomUniform(30)()).sort((a, b) => a - b);
const scale = d3.scaleSequential(d3.interpolateReds)
  .domain(d3.extent(fakeData));
d3.select("body").selectAll(null)
  .data(fakeData)
  .enter()
  .append("div")
  .style("background-color", d => scale(d))
div {
  display: inline-block;
  margin-right: 4px;
  width: 20px;
  height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
like image 182
Gerardo Furtado Avatar answered Sep 08 '25 21:09

Gerardo Furtado