Reputation: 3534
I am trying to map a series of version strings to a series of RGB colors in hex form. Given a fixed set of version strings (eg ["1.1.2", "1.1.3", ...]
) and a couple of colors (["#efebeb", "#4d4d4d", "#ff5d5d"]
) I want to create a scale that gives me an interpolated color for each version string.
I tried this:
const palette = scaleOrdinal().domain(props.sdkVersions).range(["#efebeb", "#4d4d4d", "#ff5d5d"]);
But this just alternates between the three colors:
I'm looking for smooth interpolation. When I try other scales typescript complains that it's the wrong types, or that they need to be numbers, etc. I'd rather not precalculate the list of colors since I won't know how many I'll need.
Is this possible?
Upvotes: 0
Views: 1871
Reputation: 102174
Because you have a fixed set of colours that you want to interpolate, no matter how big is the domain length, my solution involves using a linear scale to create the colours array, which you'll pass to the ordinal scale.
Therefore, you can create the linear scale with the 3 colours you want...
const linearScale = d3.scaleLinear()
.domain([0, 0.5, 1])
.range(["#efebeb", "#4d4d4d", "#ff5d5d"]);
... and then you populate the colours array, which you'll pass to the ordinal scale:
const colorArray = d3.range(data.length)
.map(d => linearScale(d / (data.length - 1)));
const ordinalScale = d3.scaleOrdinal()
.domain(data)
.range(colorArray);
The advantage of this approach is that it's dynamic, working with any number of elements in the domain.
Here is a demo with just 3 elements (that is, in your case, three versions):
const linearScale = d3.scaleLinear()
.domain([0, 0.5, 1])
.range(["#efebeb", "#4d4d4d", "#ff5d5d"]);
const data = "abc".split("");
const colorArray = d3.range(data.length).map(d=>linearScale(d/(data.length - 1)));
const ordinalScale = d3.scaleOrdinal()
.domain(data)
.range(colorArray);
const divs = d3.select("body")
.selectAll(null)
.data(data)
.enter()
.append("div")
.style("background-color", d=>ordinalScale(d))
div {
display: inline-block;
width: 18px;
height: 18px;
margin-right: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Now with 10 versions:
const linearScale = d3.scaleLinear()
.domain([0, 0.5, 1])
.range(["#efebeb", "#4d4d4d", "#ff5d5d"]);
const data = "abcdefghij".split("");
const colorArray = d3.range(data.length).map(d=>linearScale(d/(data.length - 1)));
const ordinalScale = d3.scaleOrdinal()
.domain(data)
.range(colorArray);
const divs = d3.select("body")
.selectAll(null)
.data(data)
.enter()
.append("div")
.style("background-color", d=>ordinalScale(d))
div {
display: inline-block;
width: 18px;
height: 18px;
margin-right: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
And, finally, with 50 versions:
const linearScale = d3.scaleLinear()
.domain([0, 0.5, 1])
.range(["#efebeb", "#4d4d4d", "#ff5d5d"]);
const data = d3.range(50);
const colorArray = d3.range(data.length).map(d=>linearScale(d/(data.length - 1)));
const ordinalScale = d3.scaleOrdinal()
.domain(data)
.range(colorArray);
const divs = d3.select("body")
.selectAll(null)
.data(data)
.enter()
.append("div")
.style("background-color", d=>ordinalScale(d))
div {
display: inline-block;
width: 18px;
height: 18px;
margin-right: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Upvotes: 3