var scale = d3.scaleLinear()
.domain([0,100,200])
.range(["orange","red","purple"])
scale.invertColor = function(x) {
var domain = this.domain();
var range = this.range();
// check each segment to see if the inverted value scales back to the original value:
for(var i = 0; i < domain.length - 1; i++) {
var d = [domain[i], domain[i+1]];
var r = [range[i], range[i+1]];
var inverted = (invert(x,d,r))
if (d3.color(this(inverted)).toString() == d3.color(x).toString()) return inverted;
}
return NaN; // if nothing matched
function invert(x,d,r) {
// get the color into a common form
var color = d3.color(x);
// do the same for the range:
var min = d3.color(r[0]);
var max = d3.color(r[1]);
// find out which channel offerrs the greatest spread between min and max:
var spreads = [max.r-min.r,max.g-min.g,max.b-min.g].map(function(d) { return Math.abs(d); });
var i = spreads.indexOf(Math.max(...spreads));
var channel = d3.keys(color)[i];
// The deinterpolation function
var deinterpolate = function (value,a,b) {
if(b-a==0) return 0;
return Math.abs((value - a) / (b - a))
}
// Get t
var t = deinterpolate(color[channel],min[channel],max[channel]);
// Interpolate between domain values with t to get the inverted value:
return d3.interpolate(...d)(t)
}
};
var test = [0,10,50,90,100,110,150,190,200];
console.log("Test values:");
console.log(test)
console.log("Scaling and then Inverting values:");
console.log(test.map(function(d) { return scale.invertColor(scale(d)); }));
console.log("Original values minus scaled and inverted values:");
console.log(test.map(function(d) { return (d - scale.invertColor(scale(d))); }));
<script src="https://d3js.org/d3.v5.min.js"></script>