Javascript converter HSB / HSV cor para RGB com precisão

Preciso converter HSB para RGB com precisão, mas não tenho certeza de como contornar o problema de transformar decimais em números inteiros sem arredondamento. Esta é a function atual que tenho de uma biblioteca colorpicker:

HSBToRGB = function (hsb) { var rgb = { }; var h = Math.round(hsb.h); var s = Math.round(hsb.s * 255 / 100); var v = Math.round(hsb.b * 255 / 100); if (s == 0) { rgb.r = rgb.g = rgb.b = v; } else { var t1 = v; var t2 = (255 - s) * v / 255; var t3 = (t1 - t2) * (h % 60) / 60; if (h == 360) h = 0; if (h < 60) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3 } else if (h < 120) { rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3 } else if (h < 180) { rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3 } else if (h < 240) { rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3 } else if (h < 300) { rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3 } else if (h < 360) { rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3 } else { rgb.r = 0; rgb.g = 0; rgb.b = 0 } } return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) }; 

Como você pode ver a imprecisão nesta function vem do Math.round

Do link de Parthik Gosar neste comentário com pequenas modificações para permitir que você insira cada valor independentemente ou todos de uma vez como um object

 /* accepts parameters * h Object = {h:x, s:y, v:z} * OR * h, s, v */ function HSVtoRGB(h, s, v) { var r, g, b, i, f, p, q, t; if (arguments.length === 1) { s = hs, v = hv, h = hh; } i = Math.floor(h * 6); f = h * 6 - i; p = v * (1 - s); q = v * (1 - f * s); t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) }; } 

Este código espera 0 <= h, s, v <= 1 , se você estiver usando graus ou radianos, lembre-se de dividi-los.

Os retornados 0 <= r, g, b <= 255 são arredondados para o número inteiro mais próximo. Se você não quiser esse comportamento, remova o Math.round s do object retornado.


E o reverso (com menos divisão)

 /* accepts parameters * r Object = {r:x, g:y, b:z} * OR * r, g, b */ function RGBtoHSV(r, g, b) { if (arguments.length === 1) { g = rg, b = rb, r = rr; } var max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min, h, s = (max === 0 ? 0 : d / max), v = max / 255; switch (max) { case min: h = 0; break; case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break; case g: h = (b - r) + d * 2; h /= 6 * d; break; case b: h = (r - g) + d * 4; h /= 6 * d; break; } return { h: h, s: s, v: v }; } 

Este código irá gerar 0 <= h, s, v <= 1 , mas desta vez leva qualquer 0 <= r, g, b <= 255 (não precisa ser um inteiro)


Para completar,

 function HSVtoHSL(h, s, v) { if (arguments.length === 1) { s = hs, v = hv, h = hh; } var _h = h, _s = s * v, _l = (2 - s) * v; _s /= (_l <= 1) ? _l : 2 - _l; _l /= 2; return { h: _h, s: _s, l: _l }; } function HSLtoHSV(h, s, l) { if (arguments.length === 1) { s = hs, l = hl, h = hh; } var _h = h, _s, _v; l *= 2; s *= (l <= 1) ? l : 2 - l; _v = (l + s) / 2; _s = (2 * s) / (l + s); return { h: _h, s: _s, v: _v }; } 

Todos esses valores devem estar no intervalo de 0 a 1 . Para HSL<->RGB vá pelo HSV .

Dada a crescente popularidade do npm , acho que vale a pena mencionar um pacote contendo todas essas funções através de uma API simples:

npm instala colors

 var colorsys = require('colorsys') colorsys.rgb_to_hsv({ r: 255, g: 255, b: 255 }) // { h: 0 , s: 0 , v: 100 } 

Para o navegador:

 colorsys.rgb_to_hex(h, s, v) // #hexcolor 

Eu escrevi uma vez esta function:

 function mix(a, b, v) { return (1-v)*a + v*b; } function HSVtoRGB(H, S, V) { var V2 = V * (1 - S); var r = ((H>=0 && H<=60) || (H>=300 && H<=360)) ? V : ((H>=120 && H<=240) ? V2 : ((H>=60 && H<=120) ? mix(V,V2,(H-60)/60) : ((H>=240 && H<=300) ? mix(V2,V,(H-240)/60) : 0))); var g = (H>=60 && H<=180) ? V : ((H>=240 && H<=360) ? V2 : ((H>=0 && H<=60) ? mix(V2,V,H/60) : ((H>=180 && H<=240) ? mix(V,V2,(H-180)/60) : 0))); var b = (H>=0 && H<=120) ? V2 : ((H>=180 && H<=300) ? V : ((H>=120 && H<=180) ? mix(V2,V,(H-120)/60) : ((H>=300 && H<=360) ? mix(V,V2,(H-300)/60) : 0))); return { r : Math.round(r * 255), g : Math.round(g * 255), b : Math.round(b * 255) }; } 

Ele espera 0 <= H <= 360, 0 <= S <= 1 e 0 <= V <= 1 e retorna um objeto que contém R, G e B (valores inteiros entre 0 e 255). Eu usei essa imagem para criar o código.

Há um bug na function HSVtoHSL Paul S: quando a input v é 0, temos um problema de divisão por zero e a saída s se torna NaN. Aqui está uma correção:

 function HSVtoHSL(h, s, v) { if (arguments.length === 1) { s = hs, v = hv, h = hh; } var _h = h, _s = s * v, _l = (2 - s) * v; _s /= (_l <= 1) ? (_l === 0 ? 1 : _l) : 2 - _l; _l /= 2; return { h: _h, s: _s, l: _l; }; } 

PS Eu teria acrescentado isso como um comentário no post de Paul S., mas sou novo e o sistema me disse que não tenho representante suficiente.

Aqui está o algoritmo no unityscript, você terá que rewrite as funções float, mathf.floor e a saída é um vector3 de 3 floats.

EDIT Esta foi a primeira resposta nesta página e pareceu valioso fornecer essa ajuda quando não havia outras respostas, com a pequena reserva que você precisa convertê-la em uma versão quase idêntica em JS.

 function HSVToRGB(h : float, s : float, v : float) { h=h%1; s=s%1; v=v%1; var r : float; var g : float; var b : float; var i : float; var f : float; var p : float; var q : float; var t : float; i = Mathf.Floor(h * 6); f = h * 6 - i; p = v * (1 - s); q = v * (1 - f * s); t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } return Color(r,g,b); } 
Intereting Posts