JavaScript HSL拾色器

来源:互联网 发布:文档加密软件下载 编辑:程序博客网 时间:2024/06/03 20:31

HSL 和 HSV 在数学上定义为在 RGB 空间中的颜色的 R, G 和 B 的坐标的变换。

从 RGB 到 HSL 或 HSV 的转换

设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设 min 等于这些值中的最小者。要找到在 HSL 空间中的 (h, s, l) 值,这里的 h ∈ [0, 360)是角度的色相角,而 s, l ∈ [0,1] 是饱和度和亮度,计算为:

这里写图片描述

h 的值通常规范化到位于 0 到 360°之间。而 h = 0 用于 max = min 的(就是灰色)时候而不是留下 h 未定义。
HSL 和 HSV 有同样的色相定义,但是其他分量不同。HSV 颜色的 s 和 v 的值定义如下:

这里写图片描述

从 HSL 到 RGB 的转换

给定 HSL 空间中的 (h, s, l) 值定义的一个颜色,带有 h 在指示色相角度的值域 [0, 360)中,分别表示饱和度和亮度的s 和 l 在值域 [0, 1] 中,相应在 RGB 空间中的 (r, g, b) 三原色,带有分别对应于红色、绿色和蓝色的 r, g 和 b 也在值域 [0, 1] 中,它们可计算为:
首先,如果 s = 0,则结果的颜色是非彩色的、或灰色的。在这个特殊情况,r, g 和 b 都等于 l。注意 h 的值在这种情况下是未定义的。
当 s ≠ 0 的时候,可以使用下列过程:

这里写图片描述

对于每个颜色向量 Color = (ColorR, ColorG, ColorB) = (r, g, b),

这里写图片描述

从 HSV 到 RGB 的转换

类似的,给定在 HSV 中 (h, s, v) 值定义的一个颜色,带有如上的 h,和分别表示饱和度和明度的 s 和 v 变化于 0 到 1 之间,在 RGB 空间中对应的 (r, g, b) 三原色可以计算为:

这里写图片描述

对于每个颜色向量 (r, g, b),

这里写图片描述

<html>    <style>        .childDiv {            display:inline-block;               vertical-align:middle;            margin-left: 30px;            margin-top: 10px;            margin-bottom: 10px;        }        #colorPickDiv {            background-color: WhiteSmoke;            border: 1px solid LightGrey;            padding-top: 20px;            padding-bottom: 20px;            padding-right: 30px;        }        #hueTipDiv {            margin: 0 0 10px 70px;        }        #luminanceTipDiv {            margin-top: 20px        }        #colorDiv {            width: 100px;            height: 100px;            background-color: black;        }        #valueDiv {            box-shadow: 0px -5px 10px LightGrey;            background-color: WhiteSmoke;            border: 1px solid LightGrey;            border-top-width: 0;            padding-right: 10px;            padding-bottom: 10px;        }    </style>    <body>        <div>            <div id="colorPickDiv">                <div class="childDiv">                    <div id="hueTipDiv">Hue:0</div>                    <canvas id="canvas" width="200" height="200">Your browser does not support canvas</canvas>                </div>                <div class="childDiv">                    <div id="saturationTipDiv" class="divMarginBottom">Saturation:0%</div>                    <input id="saturationRange" onChange="onHSLRangeChange()" type="range" min="0" max="100" step="1" value="100"/>                    <div id="luminanceTipDiv" class="divMarginBottom">Luminance:0%</div>                    <input id="luminanceRange" onChange="onHSLRangeChange()" type="range" min="0" max="100" step="1" value="50"/>                </div>                <div id="colorDiv" class="childDiv"></div>            </div>            <div id="valueDiv">                <div class="childDiv">                    <div id="hexadecimalTipDiv" class="divMarginBottom">Hexadecimal:</div>                    <input id="hexadecimalValueDiv" type="text" disabled="disabled"/>                </div>                <div class="childDiv">                    <div id="rgbTipDiv" class="divMarginBottom">RGB:</div>                    <input id="rgbValueDiv" type="text" readonly="readonly"/>                </div>                <div class="childDiv">                    <div id="hslTipDiv" class="divMarginBottom">HSL:</div>                    <input id="hslValueDiv" type="text" readonly="readonly"/>                </div>            </div>        </div>        <script>            var c = document.getElementById("canvas");            var ctx = c.getContext("2d");            var colorDiv = document.getElementById("colorDiv");            var hexadecimalValueDiv = document.getElementById("hexadecimalValueDiv");            var rgbValueDiv = document.getElementById("rgbValueDiv");            var hslValueDiv = document.getElementById("hslValueDiv");            var hexadecimalTipDiv = document.getElementById("hexadecimalTipDiv");            var saturationTipDiv = document.getElementById("saturationTipDiv");            var saturationRange = document.getElementById("saturationRange");            var luminanceTipDiv = document.getElementById("luminanceTipDiv");            var luminanceRange = document.getElementById("luminanceRange");            //十字光标颜色            var crossCursorColor = "black";            //十字光标线宽            var crossCursorLineWidth = 2;            //十字光标某一边线段长            var crossCursorHalfLineLen = 5;            //十字光标中间断裂处长度            var crossCursorHalfBreakLineLen = 2;            //画布中心点X坐标            var centerX = c.width / 2;            //画布中心点Y坐标            var centerY = c.height / 2;            //缩放绘制比例            var scaleRate = 10;            //画布的内切圆半径(之所以减去一个数是为了可以显示完整的十字光标)            var innerRadius = Math.min(centerX, centerY) - crossCursorHalfLineLen - crossCursorHalfBreakLineLen;            //内切圆半径的平方            var pow2InnerRadius = Math.pow(innerRadius, 2);            //缩放绘制时的绘制半径,即画布的外径除以缩放比例            var scaledRadius = Math.sqrt(Math.pow(c.width / 2, 2) + Math.pow(c.height / 2, 2)) / scaleRate;            //由于该圆是由绕圆心的多条线段组成,该值表示将圆分割的份数            var count = 360;            //一整个圆的弧度值            var doublePI = Math.PI * 2;            //由于圆心处是多条线段的交汇点,Composite是source-over模式,所以后绘制的线段会覆盖前一个线段。另外由于采用线段模拟圆,英雌            var deprecatedRadius = innerRadius * 0.3;            //废弃圆半径的平方            var pow2DeprecatedRadius = Math.pow(deprecatedRadius, 2);            //色相(0-360)            var hue;            //饱和度(0%-100%)            var saturation;             //亮度luminance或明度lightness(0%-100%)            var luminance;            //当前色相位置X坐标            var currentHuePosX = centerX + innerRadius - 1;            //当前色相位置Y坐标            var currentHuePosY = centerY;            //填充圆            function fillCircle(cx, cy, r, color) {                ctx.fillStyle = color;                ctx.beginPath();                ctx.arc(cx, cy, r, 0, doublePI);                ctx.fill();            }            //绘制线条            function strokeLine(x1, y1, x2, y2) {                ctx.beginPath();                ctx.moveTo(x1, y1);                ctx.lineTo(x2, y2);                ctx.stroke();            }            //将整数转为16进制,至少保留2位            function toHexString(intValue) {                var str = intValue.toString(16);                if(str.length == 1) {                    str = "0" + str;                }                return str;            }            //判断坐标(x,y)是否在合法的区域内            function isInValidRange(x, y) {                var pow2Distance = Math.pow(x-centerX, 2) + Math.pow(y-centerY, 2);                return pow2Distance >= pow2DeprecatedRadius && pow2Distance <= pow2InnerRadius;            }            //绘制十字光标            function strokeCrossCursor(x, y) {                ctx.globalCompositeOperation = "source-over";                ctx.strokeColor = crossCursorColor;                ctx.lineWidth = crossCursorLineWidth;                strokeLine(x, y-crossCursorHalfBreakLineLen, x, y-crossCursorHalfBreakLineLen-crossCursorHalfLineLen);                strokeLine(x, y+crossCursorHalfBreakLineLen, x, y+crossCursorHalfBreakLineLen+crossCursorHalfLineLen);                strokeLine(x-crossCursorHalfBreakLineLen, y, x-crossCursorHalfBreakLineLen-crossCursorHalfLineLen, y);                strokeLine(x+crossCursorHalfBreakLineLen, y, x+crossCursorHalfBreakLineLen+crossCursorHalfLineLen, y);            }            //将对象中的hsl分量组成一个hsl颜色(h在0到360之间,s与l均在0到1之间)            function formHslColor(obj) {                return "hsl(" + obj.h + "," + Math.round(obj.s * 1000)/10 + "%," + Math.round(obj.l * 1000)/10 + "%)";             }            //将对象中的rgb分量组成一个rgb颜色(r,g,b在0到255之间)            function formRgbColor(obj) {                return "rgb(" + [obj.r, obj.g, obj.b].join(",") + ")";            }            //从画布的某点获取存储RGB的对象            function getRgbObj(x, y) {                var w = 1;                var h = 1;                var imgData = ctx.getImageData(x,y,w,h);                var obj = {                    r: imgData.data[0],                    g: imgData.data[1],                    b: imgData.data[2],                    a: imgData.data[3]                }                return obj;            }            //将rgb转换为hsl对象()            function rgbToHslObj(r, g, b) {                r /= 255;                g /= 255;                b /= 255;                var max = Math.max(r, g, b);                var min = Math.min(r, g, b);                var diff = max - min;                var twoValue = max + min;                var obj = {h:0, s:0, l:0};                if(max == min) {                    obj.h = 0;                } else if(max == r && g >= b) {                    obj.h = 60 * (g - b) / diff;                } else if(max == r && g < b) {                    obj.h = 60 * (g - b) / diff + 360;                } else if(max == g) {                    obj.h = 60 * (b - r) / diff + 120;                } else if(max == b) {                    obj.h = 60 * (r - g) / diff + 240;                }                obj.l = twoValue / 2;                if(obj.l == 0 || max == min) {                    obj.s = 0;                } else if(0 < obj.l && obj.l <= 0.5) {                    obj.s = diff / twoValue;                    //obj.s = diff / (2 * obj.l);                } else {                    obj.s = diff / (2 - twoValue);                    //obj.s = diff / (2 - 2 * obj.l);                }                obj.h = Math.round(obj.h);                return obj;            }            //创建Hue颜色圆环            function createHueRing() {                ctx.globalCompositeOperation = "source-over";                ctx.clearRect(0,0,c.width,c.height);                ctx.save();                //将绘制原点移动到画布中心                ctx.translate(centerX, centerY);                //将画布放大相应比例,restore后,绘制内容会缩小                ctx.scale(scaleRate, scaleRate);                for(var i=0; i<count; i++) {                    var degree = i / count * 360;                    var radian = Math.PI * degree / 180;                    var x = scaledRadius * Math.cos(radian);                    var y = scaledRadius * Math.sin(radian);                    ctx.lineWidth=1;                    ctx.strokeStyle = "hsl(" + degree +"," + saturation + "," + luminance + ")";                    ctx.beginPath();                    ctx.moveTo(x, y);                    ctx.lineTo(0,0);                    ctx.stroke();                }                ctx.restore();                ctx.globalCompositeOperation = "destination-out";                fillCircle(centerX, centerY, deprecatedRadius, "black");                ctx.globalCompositeOperation = "destination-in";                fillCircle(centerX, centerY, innerRadius, "black");            }            //点击canvas中的Hue拾色圈            function onCanvasClick() {                var x = event.offsetX;                var y = event.offsetY;                if(!isInValidRange(x, y)) {                    return;                }                currentHuePosX = x;                currentHuePosY = y;                //创建hue背景圆环                createHueRing();                setColorValue(x, y);                strokeCrossCursor(x, y);            }            function setColorValue(x, y) {                //获取包含rgb的颜色对象                var rgbObj = getRgbObj(x, y);                var rgbColor = formRgbColor(rgbObj);                colorDiv.style.backgroundColor = rgbColor;                rgbValueDiv.value = rgbColor;                var hex = "#" + toHexString(rgbObj.r) + toHexString(rgbObj.g) + toHexString(rgbObj.b);                hexadecimalValueDiv.value = hex;                var hslObj = rgbToHslObj(rgbObj.r, rgbObj.g, rgbObj.b);                hslValueDiv.value = formHslColor(hslObj);                hueTipDiv.innerHTML = ("Hue:" + hslObj.h);            }            function onHSLRangeChange() {                //event.target.value;                saturation = saturationRange.value + "%";                luminance = luminanceRange.value + "%";                saturationTipDiv.innerHTML = ("Saturation:" + saturation);                luminanceTipDiv.innerHTML = ("Luminance:" + luminance);                createHueRing();                setColorValue(currentHuePosX, currentHuePosY)                strokeCrossCursor(currentHuePosX, currentHuePosY);            }            function init() {                c.addEventListener("click", onCanvasClick);                onHSLRangeChange();            }            init();        </script>    </body></html>

这里写图片描述

这里写图片描述

有几个缺陷:
- 不能根据颜色值来设置HSL。
- 由于HSL的值是根据从Hue环形调色板中取出的RGB颜色值换算为HSL的,因此跟滑动条上的值可能会有出入(如果是5舍6入那就一样了)

2 0
原创粉丝点击