Search code examples
javascriptreact-nativecolor-wheel

Converting from HSV to RGB with colorsys gives null values


I'm using react-native-color-wheel to generate a color wheel.

I have the following code:

<ColorWheel
  initialColor="#ffffff"
  onColorChange={color => console.log({color})}
  style={{ width: Dimensions.get('window').width }}
  thumbStyle={{ height: 30, width: 30, borderRadius: 30 }}
/>

The color is logged but as a HSV value like {h:-120,s:100,v:100} (corresponds to Blue on the wheel). But I need a RGB instead. I've attempted, unsuccessfully, to use colorsys, like this:

onColorChange={color => {
    var rgb = colorsys.hsvToRgb({color});
    console.log({rgb})
}}

Logged: {r:null,g:null,b:null}

Why do I get null values?


Solution

  • link in this comment with slight modification to let you enter each value independently or all at once as an 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 = h.s, v = h.v, h = h.h;
        }
        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)
        };
    }
    

    This code expects 0 <= h, s, v <= 1, if you're using degrees or radians, remember to divide them out.

    The returned 0 <= r, g, b <= 255 are rounded to the nearest Integer. If you don't want this behaviour remove the Math.rounds from the returned object.

    And the reverse (with less division)

    /* accepts parameters
     * r  Object = {r:x, g:y, b:z}
     * OR 
     * r, g, b
    */
    function RGBtoHSV(r, g, b) {
        if (arguments.length === 1) {
            g = r.g, b = r.b, r = r.r;
        }
        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
        };
    }
    

    This code will output 0 <= h, s, v <= 1, but this time takes any 0 <= r, g, b <= 255 (does not need to be an integer)

    For completeness,

    function HSVtoHSL(h, s, v) {
        if (arguments.length === 1) {
            s = h.s, v = h.v, h = h.h;
        }
        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 = h.s, l = h.l, h = h.h;
        }
        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
        };
    }
    

    All of these values should be in the range 0 to 1. For HSL<->RGB go via HSV.