溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript實(shí)現(xiàn)HSL拾色器的方法

發(fā)布時間:2020-07-22 17:43:14 來源:億速云 閱讀:273 作者:小豬 欄目:web開發(fā)

小編這次要給大家分享的是JavaScript實(shí)現(xiàn)HSL拾色器的方法,文章內(nèi)容豐富,感興趣的小伙伴可以來了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。

HSL 和 HSV 在數(shù)學(xué)上定義為在 RGB 空間中的顏色的 R, G 和 B 的坐標(biāo)的變換。

從 RGB 到 HSL 或 HSV 的轉(zhuǎn)換

設(shè) (r, g, b) 分別是一個顏色的紅、綠和藍(lán)坐標(biāo),它們的值是在 0 到 1 之間的實(shí)數(shù)。設(shè) max 等價于 r, g 和 b 中的最大者。設(shè) min 等于這些值中的最小者。要找到在 HSL 空間中的 (h, s, l) 值,這里的 h ∈ [0, 360)是角度的色相角,而 s, l ∈ [0,1] 是飽和度和亮度,計(jì)算為:

JavaScript實(shí)現(xiàn)HSL拾色器的方法

h 的值通常規(guī)范化到位于 0 到 360°之間。而 h = 0 用于 max = min 的(就是灰色)時候而不是留下 h 未定義。
HSL 和 HSV 有同樣的色相定義,但是其他分量不同。HSV 顏色的 s 和 v 的值定義如下:

JavaScript實(shí)現(xiàn)HSL拾色器的方法

從 HSL 到 RGB 的轉(zhuǎn)換

給定 HSL 空間中的 (h, s, l) 值定義的一個顏色,帶有 h 在指示色相角度的值域 [0, 360)中,分別表示飽和度和亮度的s 和 l 在值域 [0, 1] 中,相應(yīng)在 RGB 空間中的 (r, g, b) 三原色,帶有分別對應(yīng)于紅色、綠色和藍(lán)色的 r, g 和 b 也在值域 [0, 1] 中,它們可計(jì)算為:
首先,如果 s = 0,則結(jié)果的顏色是非彩色的、或灰色的。在這個特殊情況,r, g 和 b 都等于 l。注意 h 的值在這種情況下是未定義的。
當(dāng) s ≠ 0 的時候,可以使用下列過程:

JavaScript實(shí)現(xiàn)HSL拾色器的方法

對于每個顏色向量 Color = (ColorR, ColorG, ColorB) = (r, g, b),

JavaScript實(shí)現(xiàn)HSL拾色器的方法

從 HSV 到 RGB 的轉(zhuǎn)換

類似的,給定在 HSV 中 (h, s, v) 值定義的一個顏色,帶有如上的 h,和分別表示飽和度和明度的 s 和 v 變化于 0 到 1 之間,在 RGB 空間中對應(yīng)的 (r, g, b) 三原色可以計(jì)算為:

JavaScript實(shí)現(xiàn)HSL拾色器的方法

對于每個顏色向量 (r, g, b),

JavaScript實(shí)現(xiàn)HSL拾色器的方法

<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");

      //十字光標(biāo)顏色
      var crossCursorColor = "black";
      //十字光標(biāo)線寬
      var crossCursorLineWidth = 2;
      //十字光標(biāo)某一邊線段長
      var crossCursorHalfLineLen = 5;
      //十字光標(biāo)中間斷裂處長度
      var crossCursorHalfBreakLineLen = 2;

      //畫布中心點(diǎn)X坐標(biāo)
      var centerX = c.width / 2;
      //畫布中心點(diǎn)Y坐標(biāo)
      var centerY = c.height / 2;
      //縮放繪制比例
      var scaleRate = 10;
      //畫布的內(nèi)切圓半徑(之所以減去一個數(shù)是為了可以顯示完整的十字光標(biāo))
      var innerRadius = Math.min(centerX, centerY) - crossCursorHalfLineLen - crossCursorHalfBreakLineLen;
      //內(nèi)切圓半徑的平方
      var pow2InnerRadius = Math.pow(innerRadius, 2);
      //縮放繪制時的繪制半徑,即畫布的外徑除以縮放比例
      var scaledRadius = Math.sqrt(Math.pow(c.width / 2, 2) + Math.pow(c.height / 2, 2)) / scaleRate;
      //由于該圓是由繞圓心的多條線段組成,該值表示將圓分割的份數(shù)
      var count = 360;
      //一整個圓的弧度值
      var doublePI = Math.PI * 2;
      //由于圓心處是多條線段的交匯點(diǎn),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;

      //當(dāng)前色相位置X坐標(biāo)
      var currentHuePosX = centerX + innerRadius - 1;
      //當(dāng)前色相位置Y坐標(biāo)
      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();
      }

      //將整數(shù)轉(zhuǎn)為16進(jìn)制,至少保留2位
      function toHexString(intValue) {
        var str = intValue.toString(16);
        if(str.length == 1) {
          str = "0" + str;
        }
        return str;
      }

      //判斷坐標(biāo)(x,y)是否在合法的區(qū)域內(nèi)
      function isInValidRange(x, y) {
        var pow2Distance = Math.pow(x-centerX, 2) + Math.pow(y-centerY, 2);
        return pow2Distance >= pow2DeprecatedRadius && pow2Distance <= pow2InnerRadius;
      }

      //繪制十字光標(biāo)
      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(",") + ")";
      }

      //從畫布的某點(diǎn)獲取存儲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轉(zhuǎn)換為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;
      }

      //創(chuàng)建Hue顏色圓環(huán)
      function createHueRing() {
        ctx.globalCompositeOperation = "source-over";
        ctx.clearRect(0,0,c.width,c.height);
        ctx.save();
        //將繪制原點(diǎn)移動到畫布中心
        ctx.translate(centerX, centerY);
        //將畫布放大相應(yīng)比例,restore后,繪制內(nèi)容會縮小
        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");
      }

      //點(diǎn)擊canvas中的Hue拾色圈
      function onCanvasClick() {
        var x = event.offsetX;
        var y = event.offsetY;
        if(!isInValidRange(x, y)) {
          return;
        }
        currentHuePosX = x;
        currentHuePosY = y;
        //創(chuàng)建hue背景圓環(huán)
        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>

JavaScript實(shí)現(xiàn)HSL拾色器的方法

JavaScript實(shí)現(xiàn)HSL拾色器的方法

有幾個缺陷:

- 不能根據(jù)顏色值來設(shè)置HSL。
- 由于HSL的值是根據(jù)從Hue環(huán)形調(diào)色板中取出的RGB顏色值換算為HSL的,因此跟滑動條上的值可能會有出入

看完這篇關(guān)于JavaScript實(shí)現(xiàn)HSL拾色器的方法的文章,如果覺得文章內(nèi)容寫得不錯的話,可以把它分享出去給更多人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI