我正在尝试创建一个带有动态渐变的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)]);
它们正常工作,但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);
// 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>
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)]);
添加的点数越多,滑块看起来就越好。