JS HEX十六进制与RGB, HSL颜色的相互转换

这篇文章发布于 2010年03月12日,星期五,16:15,归类于 JS实例。 阅读 253794 次, 今日 13 次 22 条评论

 

一、关于颜色的表示

颜色的表示方式不知一种,从photoshop的取色面板就可以看出,如下:
颜色的表示方式 张鑫旭-鑫空间-鑫生活

上图共显示了5种颜色的表示方法:HSB,lab,RGB,CMYK与十六进制表示法

在CSS中,我们也经常与颜色打交道,其中有color(文字颜色),还有一个就是background-color背景颜色,还有就是border-color。一般,我们习惯用十六进制表示颜色,//zxx:AS中也是如此,不过是以”0x”打头,而不是”#”,例如黑色表示为:#000000;在CSS 2.1中,还支持RGB的颜色表示法,IE,Firefox等浏览器均支持,例如黑色可以表示为RGB(0, 0, 0);

在CSS3中,增加了 HSL colors HSLA colors RGBA colors表示方法,不过这些这里不讨论。

更多颜色的知识可以点击这里:维基百科-颜色

二、颜色表示方式转换的必要性

这确实是个疑问,虽说在CSS中有两种颜色表示方法,那么为什么还有进行颜色表示方式的转换呢,不都是一样的表示颜色吗?
确实,大多数情况下,转换时没有必要的,但是web页面千变万化,遇到的情况千奇百怪,是有需要转换的情况。

举个我遇到的例子吧,我去年9月份,学习Ajax的时候,做过一个Ajax+PHP多人在线聊天室聊天室(访问该页面点击这里),其中里面有个很重要的工具就是取色面板,用以改变显示的文字颜色的。见下图:
聊天室取色面板 张鑫旭-鑫空间-鑫生活

里面对颜色字符做了些处理,就是以十六进制的形式显示。做处理归根结底的原因就是浏览器的差异,差异如下:
在Firefox浏览器下(chrome同样),即使你使用了十六进制形式表示了颜色值,但是在HTML页面中,其却是以RGB形式表示的,这与IE浏览器是不一致的。也就是说,如果不做字符显示的处理的话,那么Firefox浏览器下,上面的聊天颜色面板获得的值就是类似于RGB(255,0,0)这种形式的,而不是”#FF0000″,我们可以做个简单的测试,看如下的代码。

<button style="color:#ff3300;" onclick="javascript:alert(this.style.color);">点击我</button>

结果Firefox下的显示是,如下图:
Firefox浏览器下的颜色显示 张鑫旭-鑫空间-鑫生活

而IE浏览器下则是:
IE浏览器下颜色的表示 张鑫旭-鑫空间-鑫生活

您可以自己点击下面的按钮测试一下:

可见,这种颜色表示的差异性,使得有时候,颜色转换成为的必须。

三、JavaScript颜色转换的核心

JavaScript颜色转换的核心就是进制间的转换。RGB格式其实就是十进制表示法,所以,十六进制颜色与RGB颜色的转换就是十六进制与十进制之间的转换。

十六进制转换为十进制相对容易些,核心代码如下示例:parseInt("0xFF"),其结果就是255,”0x”就表明当前是16进制,由于parseInt后面无参数,默认就是转换为10进制了。

十进制转换为16进制,核心代码如下:var num=255; num.toString(16);,其结果是FF。”16″表示数值转换为16进制字符串。

四、JavaScript颜色表示方式的转换的实现


转换代码如下,首先是RGB颜色转换为十六进制表示:

String.prototype.colorHex = function(){
    var that = this;
    //十六进制颜色值的正则表达式
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    // 如果是rgb颜色表示
    if (/^(rgb|RGB)/.test(that)) {
        var aColor = that.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",");
        var strHex = "#";
        for (var i=0; i<aColor.length; i++) {
            var hex = Number(aColor[i]).toString(16);
            if (hex.length < 2) {
                hex = '0' + hex;    
            }
            strHex += hex;
        }
        if (strHex.length !== 7) {
            strHex = that;    
        }
        return strHex;
    } else if (reg.test(that)) {
        var aNum = that.replace(/#/,"").split("");
        if (aNum.length === 6) {
            return that;    
        } else if(aNum.length === 3) {
            var numHex = "#";
            for (var i=0; i<aNum.length; i+=1) {
                numHex += (aNum[i] + aNum[i]);
            }
            return numHex;
        }
    }
    return that;
};

然后是十六进制颜色转为RGB格式:

String.prototype.colorRgb = function(){
    var sColor = this.toLowerCase();
    //十六进制颜色值的正则表达式
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    // 如果是16进制颜色
    if (sColor && reg.test(sColor)) {
        if (sColor.length === 4) {
            var sColorNew = "#";
            for (var i=1; i<4; i+=1) {
                sColorNew += sColor.slice(i, i+1).concat(sColor.slice(i, i+1));    
            }
            sColor = sColorNew;
        }
        //处理六位的颜色值
        var sColorChange = [];
        for (var i=1; i<7; i+=2) {
            sColorChange.push(parseInt("0x"+sColor.slice(i, i+2)));    
        }
        return "RGB(" + sColorChange.join(",") + ")";
    }
    return sColor;
};

上面颜色转换方法使用示意:

var sRgb = "RGB(23, 245, 56)" , sHex = "#34538b";
var sHexColor = sRgb.colorHex();
var sRgbColor = sHex.colorRgb();

其中colorHex()表示转换为十六进制方法,colorRgb()表示转为RGB颜色值的方法。

您可以狠狠地点击这里:测试demo页面

您也可以直接在下面进行测试:

十六进制颜色值:

GRB颜色值:

说明:此JavaScript实现的一个小小原则是“以统一的形式显示”,所以十六进制缩写的颜色值(如:#333)会以#333333显示。如果转为的字符格式不匹配或是值溢出返回,则会原值返回。例如:RGB(0,0,256)会返回RGB(0,0,256),#3456会返回#3456,#egd会返回#egd。

新增于2015-05-20
这里再附上HSL转RGB以及RGB转HSL的代码,出处见这里

HSL转RGB

/**
 * HSL颜色值转换为RGB. 
 * 换算公式改编自 http://en.wikipedia.org/wiki/HSL_color_space.
 * h, s, 和 l 设定在 [0, 1] 之间
 * 返回的 r, g, 和 b 在 [0, 255]之间
 *
 * @param   Number  h       色相
 * @param   Number  s       饱和度
 * @param   Number  l       亮度
 * @return  Array           RGB色值数值
 */
function hslToRgb(h, s, l) {
    var r, g, b;

    if(s == 0) {
        r = g = b = l; // achromatic
    } else {
        var hue2rgb = 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 [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

RGB转HSL

/**
 * RGB 颜色值转换为 HSL.
 * 转换公式参考自 http://en.wikipedia.org/wiki/HSL_color_space.
 * r, g, 和 b 需要在 [0, 255] 范围内
 * 返回的 h, s, 和 l 在 [0, 1] 之间
 *
 * @param   Number  r       红色色值
 * @param   Number  g       绿色色值
 * @param   Number  b       蓝色色值
 * @return  Array           HSL各值数组
 */
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];
}

这里转换的HSL并不能直接当作CSS颜色值处理,因为范围不一样。

H指Hue(色调)。 0(或360)表示红色,120表示绿色,240表示蓝色,当然可取其他数值来确定其它颜色;
S指Saturation(饱和度)。 取值为0%到100%之间的值;
L指Lightness(亮度)。 取值为0%到100%之间的值;

自己需要再转换下。

关于HSL,可以参见这里:css3-hsl-colors

补充于2018-11-23
增加个RGB转HSB/HSV的方法:

function rgbToHsv(r, g, b) {
    r = r / 255;
    g = g / 255;
    b = b / 255;
    var h, s, v;
    var min = Math.min(r, g, b);
    var max = v = Math.max(r, g, b);
    var l = (min + max) / 2;
    var difference = max - min;

    if (max == min) {
        h = 0;
    } else {
        switch (max) {
        case r:
            h = (g - b) / difference + (g < b ? 6 : 0);
            break;
        case g:
            h = 2.0 + (b - r) / difference;
            break;
        case b:
            h = 4.0 + (r - g) / difference;
            break;
        }
        h = Math.round(h * 60);
    }
    if (max == 0) {
        s = 0;
    } else {
        s = 1 - min / max;
    }
    s = Math.round(s * 100);
    v = Math.round(v * 100);
    return [h, s, v];
}

补充于2020-06-29
今天发现需要用到HSV/HSB转RGB色值的场景,补上一段代码:

// 参数arr的3个值分别对应[h, s, v]
function hsvToRgb(arr) {
    var h = arr[0], s = arr[1], v = arr[2];
    s = s / 100;
    v = v / 100;
    var r = 0, g = 0, b = 0;
    var i = parseInt((h / 60) % 6);
    var f = h / 60 - i;
    var p = v * (1 - s);
    var q = v * (1 - f * s);
    var t = v * (1 - (1 - f) * s);
    switch (i) {
        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;
        default:
            break;
    }
    r = parseInt(r * 255.0)
    g = parseInt(g * 255.0)
    b = parseInt(b * 255.0)
    return [r, g, b];
}

补充于2017年9月19日
再补充个任意色值(甚至是CSS颜色关键字)转换为RGB颜色的方法,此方法IE9+浏览器支持,基于DOM特性实现。

var colorToRgb = function (color) {
    var div = document.createElement('div');
    div.style.backgroundColor = color;
    document.body.appendChild(div);
    var c = window.getComputedStyle(div).backgroundColor;    
    document.body.removeChild(div);
    return c;
};

需要保证color是不带透明度的,否则最后得到的回事RGBA颜色,而不是RGB颜色。

五、结语

我js资历尚浅,所以代码可能会遇到以下些不足:
1、可能漏了些特殊情况;
2、在效率方面还可以进一步改进;
3、在算法上可以进一步优化;

欢迎指出这些不足。

(本篇完)

分享到:


发表评论(目前22 条评论)

  1. 清许说道:

    楼主太强了,多少年了,后来者的小年轻看了你的东西,都能收获很多,能够帮助

  2. huixu说道:

    如果16进制的颜色种夹杂着一些单词颜色 ,想统一转成rba,有什么方案

  3. white500说道:

    感谢 大佬分享!学习下

  4. fxss说道:

    HSL转RGB代码中:
    r = hue2rgb(p, q, h + 1/3);
    g = hue2rgb(p, q, h);
    b = hue2rgb(p, q, h – 1/3);
    这三行中的h应该先除以360,否则转换出来是错误的。

    h = h/360;
    r = hue2rgb(p, q, h + 1/3);
    g = hue2rgb(p, q, h);
    b = hue2rgb(p, q, h – 1/3);

  5. 阳光岛主说道:

    很赞的文章,收走了,留个脚印,谢谢。

  6. TPORL说道:

    rgb转hex有点问题
    rbg小于16时,转16进制只有一位数,导致输出有误

    源码:
    if (hex === ‘0’) { hex += hex }

    我的改进:
    hex = hex < 16 ? '0' + hex : hex

  7. Zz说道:

    您的源码里的校验hex颜色的正则是这么写的:var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;

    其中“A-f”这里应该是“A-F”,否则无法匹配大写的F。

  8. codelegant说道:

    RGB转HSL那个函数,h/=6是不应该修改为h*=60?
    H是一个角度值(0-360)

  9. 范生说道:

    这个有个BUG 在CHROME下面 有些颜色转换不了

  10. jsper说道:

    不好意思没事了,由于字符集编码问题,下载后的源码注释包括第一行代码是乱码导致的。现在可以了。感谢博主的倾情付出!

  11. JuN说道:

    谢谢!

  12. RedOrange说道:

    抱歉,昨天发的代码还应该改进一下,保证了一位数前面补0的情况:
    var colorHex = function(value) {
    var color = value.replace(/[^\d,]/g, ”).replace(/(\d+)/g, function(s, s1) {
    var v = parseInt(s1).toString(16);
    return v.length == 1 ? ‘0’+v : v;
    });
    return ‘#’+color.replace(/,/g, ”).toUpperCase();
    };

  13. RedOrange说道:

    rgb转16进制也可以这样,代码更少:
    var colorHex = function(value) {
    var color = value.replace(/[^\d,]/g, ”).replace(/(\d+)/g, function(s, s1) {
    return parseInt(s1).toString(16);
    });
    return ‘#’+color.replace(/,/g, ”).toUpperCase();
    };

  14. 熊熊说道:

    不错 谢了

  15. thank you!说道:

    谢谢!not bad!