The mathematical formula for lightness match
Depending on the hue prime value, the r
, g
, and b
values will map to C
, X
, and 0
:
The mathematical formula for RGB values without accounting for lightness
Lastly, we need to map each value to adjust for lightness:
The mathematical formula to account for lightness with RGB
Putting all of this together into a JavaScript function:
const hslToRgb = (h,s,l) => {
const C = (1 - Math.abs(2 * l - 1)) * s;
const hPrime = h / 60;
const X = C * (1 - Math.abs(hPrime % 2 - 1));
const m = l - C/2;
const withLight = (r,g,b) => [r+m, g+m, b+m];
Creating a color object
For ease of access when manipulating their attributes, we will be dealing with a JavaScript object. This can be created by wrapping the previously written functions:
const rgbToObject = (red,green,blue) => {
const [hue, saturation, lightness] = rgbToHsl(red, green, blue);
return {red, green, blue, hue, saturation, lightness};
}
Example
I highly encourage you to spend some time playing with this example. Seeing how each of the attributes interacts when you adjust the others can give you a deeper understanding of how the two color models fit together.
Color manipulation
Now that we have the ability to convert between color models, let’s look at how we can manipulate these colors!
Update attributes
Each of the color attributes we have covered can be manipulated individually, returning a new color object. For example, we can write a function that rotates the hue angle:
const rotateHue = rotation => ({hue, ...rest}) => {
const modulo = (x, n) => (x % n + n) % n;
const newHue = modulo(hue + rotation, 360);
The rotateHue
function accepts a rotation
parameter and returns a new function, which accepts and returns a color object. This allows for the easy creation of new “rotation” functions:
const rotate30 = rotateHue(30);
const getComplementary = rotateHue(180);
Along the same lines, you can write functions to saturate
or lighten
a color — or, inversely, desaturate
or darken
.
const saturate = x => ({saturation, ...rest}) => ({
...rest,
saturation: Math.min(1, saturation + x),
});
Color predicates
In addition to color manipulation, you can write “predicates” — that is, functions that return a Boolean value.
const isGrayscale = ({saturation}) => saturation === 0;
const isDark = ({lightness}) => lightness < .5;
Dealing with color arrays
Filters
The JavaScript [].filter
method accepts a predicate and returns a new array with all the elements that “pass.” The predicates we wrote in the previous section can be used here:
const colors = [/* ... an array of color objects ... */];
const isLight = ({lightness}) => lightness > .5;
const lightColors = colors.filter(isLight);
Sorting
To sort an array of colors, you first need to write a “comparator” function. This function takes two elements of an array and returns a number to denote the “winner.” A positive number indicates that the first element should be sorted first, and a negative indicates the second should be sorted first. A zero value indicates a tie.
For example, here is a function for comparing the lightness of two colors:
const compareLightness = (a,b) => a.lightness - b.lightness;
Here is a function that compares saturation:
const compareSaturation = (a,b) => a.saturation - b.saturation;
In an effort to prevent duplication in our code, we can write a higher-order function to return a comparison function to compare any attribute:
const compareAttribute = attribute =>
(a,b) => a[attribute] - b[attribute];
Averaging attributes
You can average the specific attributes of an array of colors by composing various JavaScript array methods. First, you can calculate the average of an attribute by summing with reduce and dividing by the array length:
const colors = [/* ... an array of color objects ... */];
const toSum = (a,b) => a + b;
const toAttribute = attribute => element => element[attribute];
const averageOfAttribute = attribute => array =>
array.map(toAttribute(attribute)).reduce(toSum) / array.length;
You can use this to “normalize” an array of colors:
/* ... continuing */
const normalizeAttribute = attribute => array => {
const averageValue = averageOfAttribute(attribute)(array);
const normalize = overwriteAttribute(attribute)(averageValue);
return normalize(array);
}
Conclusion
Colors are an integral part of the web. Breaking down colors into their attributes allows for the smart manipulation of colors and opens the door to all sorts of possibilities.
Plug: LogRocket, a DVR for web apps
[
](https://logrocket.com/signup/)
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
- 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
- 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程
本文著作权归作者所有,如若转载,请注明出处
转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com