2

I am trying to create a Lab color space slider with a dynamic gradient on the slider. I successfully created the slider (with the help of this library).

I'm having trouble creating the gradients for it. Here's the code relevant to the gradient creation:

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

They work as they should, but the colors for A and B (Lab) are wrong. What's the correct way to generate the correct gradient colors?

Here's a website of how the colors should look: 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);
          // http://stackoverflow.com/questions/2940054/
        }, 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>
Horay
  • 1,388
  • 2
  • 19
  • 36
  • When I go to your fist link and chose some randon setup for Lab, and try to reproduce the same set up in codePen, I get the same.... whats wrong? – Ander Biguri May 09 '16 at 23:17
  • I'm not exactly sure what you're asking. If you're asking what's the diff between the codepen, and http://colorizer.org/, there's a diff in the middle of the sliders a and b. Let me know if I answered your question – Horay May 09 '16 at 23:21
  • I cannot see any difference between 3 sliders in colorizer and the 3 sliders in your codepen. If there is, you need to be more specific on how to reproduce it – Ander Biguri May 09 '16 at 23:25
  • @AnderBiguri Try putting all 3 sliders to the very far right side. Look at the gradient colors of slider a. (L**a**b). In colorizer.org, it goes from left to right) green to yellow to orange/red. In the code pen it goes from green to orange/red. – Horay May 09 '16 at 23:28
  • I see, However the color i correctly chosen, If you put the slider around the area that is supposed to be yellow, it picks yellow color – Ander Biguri May 09 '16 at 23:32
  • @AnderBiguri True, but the gradient shows the wrong colors. You're right that the result color is the correct one, but I want the gradients to show the correct colors. – Horay May 09 '16 at 23:34
  • While my knoledge of javascript is almost zero, here it is my wild guess: SetGradient makes a gradient from color A to color B, however, the way you want the slider to look like, it needs a gradient that goes trhough various colors. You need to modify the code, somehow, to do that. I dont know how to do it though. However almost 100% sure that the problem is that. If you move the sliders they will always change from the color on the left to the rigth uniformily without going trhough another color. – Ander Biguri May 09 '16 at 23:34
  • 2
    Please don't post comments in [other question](http://stackoverflow.com/questions/33600134/plot-matlab-colours-as-vertical-bars/33600913)s to attract people to your question that only seems indirectly related. – horchler May 10 '16 at 02:43

1 Answers1

3

setgradient make a gradient going through the points you define. In your code, you just define the colors in the corners of your slider, so everything in the middle is out of control for you. You can improve the result by adding more points in the way of the gradient as:

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

The more points you add, the better the sliderbar would look like.

Ander Biguri
  • 35,140
  • 11
  • 74
  • 120