颜色滑块渐变

Color Slider Gradient

我正在尝试创建一个 Lab color space slider with a dynamic gradient on the slider. I successfully created the slider (with the help of this library)。

我在为其创建渐变时遇到问题。这是与渐变创建相关的代码:

setGradient(slider[0], "right", [Laba2cssString(0, Lab.a, Lab.b), Laba2cssString(100, Lab.a, Lab.b)]);
setGradient(slider[1], "right", [Laba2cssString(Lab.L, -128, Lab.b), Laba2cssString(Lab.L, 128, Lab.b)]);
setGradient(slider[2], "right", [Laba2cssString(Lab.L, Lab.a, -128), Laba2cssString(Lab.L, Lab.a, 128)]);

它们正常工作,但 A 和 B (Lab) 的颜色是错误的。生成正确渐变颜色的正确方法是什么?

这是一个关于颜色应该如何显示的网站:colorizer.org

codePen

    var myColor = new Colors(),
      overallSlidersWrapper = document.getElementById('overallSlidersWrapper'),
      slider = document.getElementsByClassName('slider'),
      type,
      mode,
      isLabAB = false,
      currentModeType,
      startPoint,
      currentTarget,
      currentTargetWidth,
      maxReal = {
        Lab: {
          L: 100,
          a: 256,
          b: 256
        }
      };
    var toCSSstring = {
      Lab: Laba2cssString
    };

    var sliderDown = function(e) {
        e.preventDefault();
        if (e.target.classList.contains('sliderRange') || e.target.classList.contains('sliderCursor')) currentTarget = e.target.parentNode;
        else if (e.target.classList.contains('slider')) currentTarget = e.target;
        else if (e.target.classList.contains('leftRoundness')) currentTarget = e.target.nextElementSibling;
        else if (e.target.classList.contains('rightRoundness')) currentTarget = e.target.previousElementSibling;
        else return;


        currentModeType = getModeType(currentTarget);
        type = currentModeType.type;
        mode = currentModeType.mode;
        isLabAB = currentModeType.isLabAB;
        startPoint = getOrigin(currentTarget);

        sliderMove(e);
        addEvent(window, 'mousemove', sliderMove);
        startRender();
      },
      sliderMove = function(e) {
        var newColor = {};
        newColor[mode] = (e.clientX - startPoint.left) /
          currentTarget.offsetWidth * maxReal[type][mode] - (isLabAB ? 128 : 0);
        myColor.setColor(newColor, type);
      };
    renderColorSliders = function(color) {
      for (var n = slider.length; n--;) {
        var currentModeType = getModeType(slider[n]),
          localType = currentModeType.type,
          localMode = currentModeType.mode,
          isLabAB = currentModeType.isLabAB;

        var colorNumber = myColor.colors.RND[localType][localMode],
          percentPosition = (((colorNumber / maxReal[localType][localMode]) + (isLabAB ? 0.5 : 0)) *
            slider[n].offsetWidth) - 7;

        var colorNumber = (localMode === 'alpha') ? myColor.colors.alpha : myColor.colors.RND[localType][localMode],
          percentPosition = (((colorNumber / maxReal[localType][localMode]) + (isLabAB ? 0.5 : 0)) *
            slider[n].offsetWidth) - 7;
        slider[n].firstElementChild.style.transform = 'translateX(' + percentPosition + 'px)';

        slider[n].firstElementChild.style.borderColor = color.RGBLuminance > 0.22 ? 'black' : 'white';
      }
    };

    var result = document.getElementById('result');

    function renderResult(color) {
      result.style.backgroundColor = rgba2cssString(color.RND.rgb.r, color.RND.rgb.g, color.RND.rgb.b);
    }

    function renderGradients(color) {
      Lab = color.RND.Lab;

      setGradient(slider[0], "right", [Laba2cssString(0, Lab.a, Lab.b), Laba2cssString(100, Lab.a, Lab.b)]);
      setGradient(slider[1], "right", [Laba2cssString(Lab.L, -128, Lab.b), Laba2cssString(Lab.L, 128, Lab.b)]);
      setGradient(slider[2], "right", [Laba2cssString(Lab.L, Lab.a, -128), Laba2cssString(Lab.L, Lab.a, 128)]);

      slider[0].previousElementSibling.style.backgroundColor = Laba2cssString(0, Lab.a, Lab.b);
      slider[0].nextElementSibling.style.backgroundColor = Laba2cssString(100, Lab.a, Lab.b);
      slider[1].previousElementSibling.style.backgroundColor = Laba2cssString(Lab.L, -128, Lab.b);
      slider[1].nextElementSibling.style.backgroundColor = Laba2cssString(Lab.L, 128, Lab.b);
      slider[2].previousElementSibling.style.backgroundColor = Laba2cssString(Lab.L, Lab.a, -128);
      slider[2].nextElementSibling.style.backgroundColor = Laba2cssString(Lab.L, Lab.a, 128);
    }

    addEvent(overallSlidersWrapper, 'mousedown', sliderDown);

    function removeMouseUpEvents() {
      removeEvent(window, 'mousemove', sliderMove);
      stopRender();
    }
    addEvent(window, 'mouseup', removeMouseUpEvents);

    var doRender = function(color) {
        renderColorSliders(color);
        renderResult(color);
        renderGradients(color);
      },
      renderTimer,
      startRender = function() {
        renderTimer = setInterval(function() {
          doRender(myColor.colors);
          // 
        }, 13); // 1000 / 60); // ~16.666 -> 60Hz or 60fps
      },
      stopRender = function() {
        clearInterval(renderTimer);
      };
    doRender(myColor.colors);

    /*-----------------------------*/
    /*------ Function Helpers -----*/
    /*-----------------------------*/

    function getOrigin(elm) {
      var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
          top: 0,
          left: 0
        },
        doc = elm && elm.ownerDocument,
        body = doc.body,
        win = doc.defaultView || doc.parentWindow || window,
        docElem = doc.documentElement || body.parentNode,
        clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
        clientLeft = docElem.clientLeft || body.clientLeft || 0;

      return {
        left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
        top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
      };
    }

    function addEvent(obj, type, func) {
      addEvent.cache = addEvent.cache || {
        _get: function(obj, type, func, checkOnly) {
          var cache = addEvent.cache[type] || [];

          for (var n = cache.length; n--;) {
            if (obj === cache[n].obj && '' + func === '' + cache[n].func) {
              func = cache[n].func;
              if (!checkOnly) {
                cache[n] = cache[n].obj = cache[n].func = null;
                cache.splice(n, 1);
              }
              return func;
            }
          }
        },
        _set: function(obj, type, func) {
          var cache = addEvent.cache[type] = addEvent.cache[type] || [];

          if (addEvent.cache._get(obj, type, func, true)) {
            return true;
          } else {
            cache.push({
              func: func,
              obj: obj
            });
          }
        }
      };

      if (!func.name && addEvent.cache._set(obj, type, func) || typeof func !== 'function') {
        return;
      }

      if (obj.addEventListener) obj.addEventListener(type, func, false);
      else obj.attachEvent('on' + type, func);
    }

    function removeEvent(obj, type, func) {
      if (typeof func !== 'function') return;
      if (!func.name) {
        func = addEvent.cache._get(obj, type, func) || func;
      }

      if (obj.removeEventListener) obj.removeEventListener(type, func, false);
      else obj.detachEvent('on' + type, func);
    }

    function hasClass(ele, cls) {
      return ele.className.match(new RegExp('(\s|^)' + cls + '(\s|$)'));
    }

    function getModeType(elem) {
      var id = elem.id, // rgbR
        len = id.length - 1, // 3
        type = id.substr(0, len), // rgb
        mode = id.charAt(len), // r
        isLabAB = type === 'Lab' && (/(?:a|b)/.test(mode)); //is 'Lab && ()'a' || 'b')

      if (elem.id === 'rgbA') mode = 'alpha';

      return {
        type: type,
        mode: mode,
        isLabAB: isLabAB
      };
    }

    /**
     * Formats the given RGB values into a string that can be used in CSS
     */
    function rgba2cssString(r, g, b, a) {
      if (r == null) return;

      if (isObject(r)) r = Object.keys(r).map(function(key) {
        return r[key]
      });
      if (Array.isArray(r)) {
        // Check if array doesn't have alpha
        if (r.length === 3) return rgba2cssString(r[0], r[1], r[2]);
        // Check if array has alpha
        else if (r.length === 4) return rgba2cssString(r[0], r[1], r[2], r[3]);
      }

      if (a || a === 0) return "rgba(" + r + "," + g + "," + b + "," + a + ")";
      return "rgb(" + r + "," + g + "," + b + ")";
    }

    /**
     * Formats the given HSL values into a string that can be used in CSS
     */
    function hsla2cssString(h, s, l, a) {
      if (h == null) return;

      if (isObject(h)) h = Object.keys(h).map(function(key) {
        return h[key]
      });
      if (Array.isArray(h)) {
        // Check if array doesn't have alpha
        if (h.length === 3) return rgba2cssString(h[0], h[1], h[2]);
        // Check if array has alpha
        else if (h.length === 4) return rgba2cssString(h[0], h[1], h[2], h[3]);
      }

      if (a || a === 0) return "hsla(" + h + "," + s + "%," + l + "%," + a + ")";
      return "hsl(" + h + "," + s + "%," + l + "%)";
    }

    /**
     * Formats the given HSV values into a string that can be used in CSS
     */
    function hsva2cssString(h, s, v, a) {
      if (h == null) return;
      var hsvObject,
        alpha;

      if (isObject(h)) {
        hsvObject = h;
        alpha = h.a;
      } else if (Array.isArray(h)) {
        hsvObject = {
          h: h[0],
          s: h[1],
          v: h[2]
        };
        alpha = h[3];
      } else if (s != null) {
        hsvObject = {
          h: h,
          s: s,
          v: v
        };
        alpha = a;
      }

      var rgbColor = myColor.convertColor(hsvObject, 'HSV2RGB');
      rgbColor.a = alpha;
      return rgba2cssString(rgbColor);
    }

    /**
     * Formats the given Lab values into a string that can be used in CSS
     */
    function Laba2cssString(L, a, b, alpha) {
      if (L == null) return;
      var LabObject,
        alphaLocal;

      if (isObject(L)) {
        LabObject = L;
        alphaLocal = L.alpha;
      } else if (Array.isArray(L)) {
        LabObject = {
          L: L[0],
          a: L[1],
          b: L[2]
        };
        alphaLocal = L[3];
      } else if (a != null) {
        LabObject = {
          L: L,
          a: a,
          b: b
        };
        alphaLocal = alpha;
      }

      var rgbColor = myColor.convertColor(LabObject, 'Lab2RGB');
      rgbColor.a = alpha;
      return rgba2cssString(rgbColor);
    }

    function setGradient(el, direction, steps, multipleBG) {
      var gradientString = "linear-gradient(to " + direction + ",";

      stepSize = 100 / (steps.length - 1);

      for (var i = 0; i < steps.length; i++) {
        gradientString += (i > 0 ? "," : "") + steps[i] + (i * stepSize) + "%";
      }
      gradientString += ")";

      if (multipleBG) {
        gradientString += ', ' + multipleBG;
      }
      el.style.backgroundImage = gradientString;
    }

    function isObject(obj) {
      return (typeof obj === "object" && !Array.isArray(obj) && obj !== null);
    }
#overallSlidersWrapper {
  width: 500px;
}
.sliderContent {
  height: 138px;
}
.colorSliderTabsLabel {
  width: calc(100% /4);
  display: inline-block;
  text-align: center;
  cursor: pointer;
}
#colorSliderTabUnderliner {
  height: 3px;
  width: calc(100% /4);
  background-color: green;
  transition: transform 0.3s cubic-bezier(0.45, 0.05, 0.55, 0.95);
}
#tabContentWrapper {
  width: 680px;
  align-items: flex-start;
  position: relative;
}
#overallSlidersWrapper {} .sliderOuterWrapper {
  margin-bottom: 10px;
}
.sliderLabel {} .sliderInnerWrapper {
  height: 18px;
  width: 100%;
  cursor: pointer;
  position: relative;
  display: flex;
}
.slider {
  height: 100%;
  width: calc(100% - 62px);
  /* Subtract TextField (44px) and Both Rounders for Slider (19px each)*/
  position: relative;
  border: 1px solid black;
  border-right: none;
  border-left: none;
}
.leftRoundness,
.rightRoundness {
  width: 9px;
  height: 100%;
  border: 1px solid black;
}
.leftRoundness {
  border-right: none;
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
}
.rightRoundness {
  border-left: none;
  border-top-right-radius: 10px;
  border-bottom-right-radius: 10px;
}
.sliderCursor {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  position: relative;
  border: 2px solid black;
}
#result {
  width: 100px;
  height: 100px;
}
<script src="https://rawgit.com/PitPik/colorPicker/master/colors.js"></script>
<div id="result"></div>

<div id="overallSlidersWrapper">
  <div id="LabSliderContent" class="sliderContent">
    <div class="sliderOuterWrapper">
      <div class="sliderLabel">Lightness</div>
      <div class="sliderInnerWrapper">
        <div class="leftRoundness"></div>
        <div id="LabL" class="slider">
          <div class="sliderCursor"></div>
        </div>
        <div class="rightRoundness"></div>
      </div>
    </div>
    <div class="sliderOuterWrapper">
      <div class="sliderLabel">a (Green ↔ Red)</div>
      <div class="sliderInnerWrapper">
        <div class="leftRoundness"></div>
        <div id="Laba" class="slider">
          <div class="sliderCursor"></div>
        </div>
        <div class="rightRoundness"></div>
      </div>
    </div>
    <div class="sliderOuterWrapper">
      <div class="sliderLabel">b (Blue ↔ Yellow)</div>
      <div class="sliderInnerWrapper">
        <div class="leftRoundness"></div>
        <div id="Labb" class="slider">
          <div class="sliderCursor"></div>
        </div>
        <div class="rightRoundness"></div>
      </div>
    </div>
  </div>
</div>

setgradient 使渐变穿过您定义的点。在您的代码中,您只需定义滑块角部的颜色,因此中间的一切都不受您的控制。您可以通过在渐变方式中添加更多点来改善结果:

setGradient(slider[0], "right", [Laba2cssString(0, Lab.a, Lab.b), Laba2cssString(50, Lab.a, Lab.b), Laba2cssString(100, Lab.a, Lab.b)]);
setGradient(slider[1], "right", [Laba2cssString(Lab.L, -128, Lab.b),Laba2cssString(Lab.L, -0, Lab.b), Laba2cssString(Lab.L, 128, Lab.b)]);
setGradient(slider[2], "right", [Laba2cssString(Lab.L, Lab.a, -128),Laba2cssString(Lab.L, Lab.a, -0), Laba2cssString(Lab.L, Lab.a, 128)]);

添加的点越多,滑块看起来就越好。