我正在创建一个使用NASA API可视化星标的应用。星的颜色以0到1的值返回,0表示纯蓝色,1表示纯红色。基本上我需要设置一种方法将javascript中的0-1值转换为滑动HEX(或rgb)比例,如下所示:
0: blue (9aafff)
.165: blue white (cad8ff)
.33: white (f7f7ff)
.495: yellow white (fcffd4)
.66: yellow (fff3a1)
.825: orange (ffa350)
1: red (fb6252)
这可能吗?我不知道如何开始接近这个。干杯!
答案 0 :(得分:2)
最好的工作是在另一个颜色空间而不是RGB颜色空间。例如HSL。
示例:
var stones = [ // Your Data
{v:0, hex:'#9aafff'},
{v:.165, hex:'#cad8ff'},
{v:.33, hex:'#f7f7ff'},
{v:.495, hex:'#fcffd4'},
{v:.66, hex:'#fff3a1'},
{v:.825, hex:'#ffa350'},
{v:1, hex:'#fb6252'},
]
stones.forEach(function(s){
s.rgb = hexToRgb(s.hex);
s.hsl = rgbToHsl.apply(0, s.rgb);
});
function valueToRgbColor(val){
for (var i=1; i<stones.length; i++) {
if (val<=stones[i].v) {
var k = (val-stones[i-1].v)/(stones[i].v-stones[i-1].v),
hsl = interpolArrays(stones[i-1].hsl, stones[i].hsl, k);
return 'rgb('+hslToRgb.apply(0,hsl).map(function(v){ return v|0})+')';
}
}
throw "bad value";
}
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSL representation
*/
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [r * 255, g * 255, b * 255];
}
function hexToRgb(hex) {
return /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
.slice(1).map(function(v){ return parseInt(v,16) });
}
function interpolArrays(a,b,k){
var c = a.slice();
for (var i=0;i<a.length;i++) c[i]+=(b[i]-a[i])*k;
return c;
}
var stones = [ // Your Data
{v:0, hex:'#9aafff'},
{v:.165, hex:'#cad8ff'},
{v:.33, hex:'#f7f7ff'},
{v:.495, hex:'#fcffd4'},
{v:.66, hex:'#fff3a1'},
{v:.825, hex:'#ffa350'},
{v:1, hex:'#fb6252'},
]
stones.forEach(function(s){
s.rgb = hexToRgb(s.hex);
s.hsl = rgbToHsl.apply(0, s.rgb);
});
function valueToRgbColor(val){
for (var i=1; i<stones.length; i++) {
if (val<=stones[i].v) {
var k = (val-stones[i-1].v)/(stones[i].v-stones[i-1].v),
hsl = interpolArrays(stones[i-1].hsl, stones[i].hsl, k);
return 'rgb('+hslToRgb.apply(0,hsl).map(function(v){ return v|0})+')';
}
}
throw "bad value";
}
for (var i=0; i<=1; i+=.03) {
var color = valueToRgbColor(i);
$('<div>').css({background:color}).text(i.toFixed(2)+" -> "+color).appendTo('body');
}
body {
background: #222;
}
div {
width:200px;
margin:auto;
color: #333;
padding: 2px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
在这个例子中,我使用了颜色空间转换函数here但是一旦你知道要查找什么就很容易找到。
请注意,现代浏览器了解HSL颜色(例如:background: hsl(120,100%, 50%);
),因此,如果您只是构建HTML,则不必在页面中嵌入所有这些代码,只需预先计算颜色停止和插值直接关于HSL值。
答案 1 :(得分:0)
这是我刚才做的纯Javascript解决方案之一。它处理两种颜色之间的线性插值。
/*
NASA color to RGB function
by Alexis Paques
It process a linear interpolation between two colors, here is the scheme:
0: blue
.165: blue white
.33: white
.495: yellow white
.66: yellow
.825: orange
1: red
*/
var blue = [0,0,255];
var bluewhite = [127,127,255];
var white = [255,255,255];
var yellowwhite = [255,255,127];
var yellow = [255,255,0];
var orange = [255,127,0];
var red = [255,0,0];
function color01toRGB(color01){
var RGB = [0,0,0];
var fromRGB = [0,0,0];
var toRGB = [0,0,0];
if(!color01)
return '#000000';
if(color01 > 1 || color01 < 0)
return '#000000';
if(color01 >= 0 && color01 <= 0.165 ){
fromRGB = blue;
toRGB = bluewhite;
}
else if(color01 > 0.165 && color01 <= 0.33 ){
fromRGB = bluewhite;
toRGB = white;
}
else if(color01 > 0.33 && color01 <= 0.495 ){
fromRGB = white;
toRGB = yellowwhite;
}
else if(color01 > 0.495 && color01 <= 0.66 ){
fromRGB = yellowwhite;
toRGB = yellow;
}
else if(color01 > 0.66 && color01 <= 0.825 ){
fromRGB = yellow;
toRGB = orange;
}
else if(color01 > 0.825 && color01 <= 1 ){
fromRGB = orange;
toRGB = red;
}
// 0.165
for (var i = RGB.length - 1; i >= 0; i--) {
RGB[i] = Math.round(fromRGB[i]*color01/0.165 + toRGB[i]*(1-color01/0.165)).toString(16);
};
return '#' + RGB.join('');
}
答案 2 :(得分:0)
由于您有一个值列表,所有值都非常饱和且明亮,因此您可以在当前(RGB)空间中进行插值。它不会像你转换为HSL一样漂亮,但是对于你拥有的颜色会很好。
由于您在数据中没有任何加权或曲线,因此使用简单的线性插值应该可以正常工作。类似的东西:
var stops = [
[0, 154, 175, 255],
[0.165, 202, 216, 255],
[0.33, 247, 247, 255],
[0.495, 252, 255, 212],
[0.66, 255, 243, 161],
[0.825, 255, 163, 80],
[1, 251, 98, 82]
];
function convertColor(color) {
var c = Math.min(Math.max(color, 0), 1); // Clamp between 0 and 1
// Find the first stop below c
var startIndex = 0;
for (; stops[startIndex][0] < c && startIndex < stops.length; ++startIndex) {
// nop
}
var start = stops[startIndex];
console.log('using stop', startIndex, 'as start');
// Find the next stop (above c)
var stopIndex = startIndex + 1;
if (stopIndex >= stops.length) {
stopIndex = stops.length - 1;
}
var stop = stops[stopIndex];
console.log('using stop', stopIndex, 'as stop');
// Find the distance from start to c and start to stop
var range = stop[0] - start[0];
var diff = c - start[0];
// Convert diff into a ratio from start to stop
if (range > 0) {
diff /= range;
}
console.log('interpolating', c, 'between', stop[0], 'and', start[0], 'by', diff);
// Convert from RGB to HSL
var a = rgbToHsl(start[1], start[2], start[3]);
var b = rgbToHsl(stop[1], stop[2], stop[3]);
console.log('hsl stops', a, b);
// Interpolate between the two colors (start * diff + (stop * (1 - diff)))
var out = [0, 0, 0];
out[0] = a[0] * diff + (b[0] * (1 - diff));
out[1] = a[1] * diff + (b[1] * (1 - diff));
out[2] = a[2] * diff + (b[2] * (1 - diff));
console.log('interpolated', out);
// Convert back from HSL to RGB
var r = hslToRgb(out[0], out[1], out[2]);
r = r.map(function(rv) {
// Round each component of the output
return Math.round(rv);
});
return r;
}
// Set the divs
var divs = document.querySelectorAll('.star');
Array.prototype.forEach.call(divs, function(star) {
var color = convertColor(star.dataset.color);
var colorStr = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
console.log('setting', star, 'to', colorStr);
star.style.backgroundColor = colorStr;
});
// HSL to RGB conversion from http://stackoverflow.com/a/30758827/129032
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, l];
}
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [r * 255, g * 255, b * 255];
}
&#13;
.star {
width: 24px;
height: 24px;
display: inline-block;
box-shadow: 0px 0px 16px -2px rgba(0, 0, 0, 0.66);
}
&#13;
<div class="star" data-color="0.0"></div>
<div class="star" data-color="0.05"></div>
<div class="star" data-color="0.1"></div>
<div class="star" data-color="0.15"></div>
<div class="star" data-color="0.2"></div>
<div class="star" data-color="0.25"></div>
<div class="star" data-color="0.3"></div>
<div class="star" data-color="0.35"></div>
<div class="star" data-color="0.4"></div>
<div class="star" data-color="0.45"></div>
<div class="star" data-color="0.5"></div>
<div class="star" data-color="0.55"></div>
<div class="star" data-color="0.6"></div>
<div class="star" data-color="0.65"></div>
<div class="star" data-color="0.7"></div>
<div class="star" data-color="0.75"></div>
<div class="star" data-color="0.8"></div>
<div class="star" data-color="0.85"></div>
<div class="star" data-color="0.9"></div>
<div class="star" data-color="0.95"></div>
<div class="star" data-color="1.0"></div>
&#13;