Skip to content

Commit

Permalink
feat: ES3 compatibility.
Browse files Browse the repository at this point in the history
  • Loading branch information
miyaokamarina committed Nov 18, 2019
1 parent 2055f6e commit 27f9a77
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 56 deletions.
17 changes: 1 addition & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> Minimal (≈650 bytes minigzip) and fast (≈13% faster than original)
> [HSLᵤᵥ](http://hsluv.org) implementation with TypeScript support.
```shell script
```bash
yarn add @ripreact/hsl
```

Expand All @@ -15,21 +15,6 @@ import { hsl } from '@ripreact/hsl';
console.log(hsl(0, 100, 50, 0.5)); // → `'#ea006480'`
```

## Environment

This module uses some ES6+ features:

- arrow functions,
- array and object destructuring,
- default parameters,
- exponentiation operator,
- template literals,
- `Array#{map,reduce}`.

If you need old environments support, you should transpile this module and add
polyfills when needed. When using webpack and Babel, you can add
`include: [require.resolve('@ripreact/hsl')]` to Babel’s rule.

## License

MIT
2 changes: 1 addition & 1 deletion examples/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const { hsl } = require('..');
const { hsl } = require('@ripreact/hsl');

console.log(hsl(0, 100, 50, 0.5));
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ripreact/hsl",
"version": "1.1.0",
"version": "1.1.1",
"description": "Minimal and fast HSLᵤᵥ implementation.",
"repository": "https://github.com/ripreact/hsl",
"author": "Marina Miyaoka <[email protected]> (https://twitter.com/miyaokamarina)",
Expand Down
2 changes: 1 addition & 1 deletion prettier.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
semi: true,
singleQuote: true,
tabWidth: 4,
trailingComma: 'all',
trailingComma: 'none',
arrowParens: 'avoid',
proseWrap: 'always',
overrides: [
Expand Down
10 changes: 5 additions & 5 deletions src/main.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
declare module '@ripreact/hsl' {
/**
* Minimal and fast HSLᵤᵥ implementation. Returns `#rrggbbaa` string.
* Minimal and fast HSLᵤᵥ implementation. Returns `'#rrggbbaa'` string.
*
* @param h HSLᵤᵥ hue.
* @param s HSLᵤᵥ saturation.
* @param l HSLᵤᵥ lightness.
* @param a Alpha.
* @param h HSLᵤᵥ hue (0..360).
* @param s HSLᵤᵥ saturation (0..100).
* @param l HSLᵤᵥ lightness (0..100).
* @param [a] Alpha (0..1).
*/
export function hsl(h: number, s: number, l: number, a?: number): string;
}
69 changes: 37 additions & 32 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

Object.defineProperty(exports, '__esModule', { value: true });

let { sin, cos, min, max, round, PI } = Math;
let normalize = a => round(max(0, min(a, 1)) * 255);
function normalize(a) {
return Math.round(Math.max(0, Math.min(a, 1)) * 255);
}

let M = [
var M = [
3.240969941904521,
-1.537383177570093,
-0.498610760293,
Expand All @@ -16,32 +17,34 @@ let M = [

0.055630079696993,
-0.20397695888897,
1.056971514242878,
1.056971514242878
];

let q = 126452;
let r = 769860;
var q = 126452;
var r = 769860;

var pad = '00000000';

/**
* Minimal and fast HSLᵤᵥ implementation. Returns `#rrggbbaa` string.
* Minimal and fast HSLᵤᵥ implementation. Returns `'#rrggbbaa'` string.
*
* @param h HSLᵤᵥ hue (0..360, will be normalized).
* @param s HSLᵤᵥ saturation.
* @param l HSLᵤᵥ lightness.
* @param a Alpha.
* @param {number} h HSLᵤᵥ hue (0..360).
* @param {number} s HSLᵤᵥ saturation (0..100).
* @param {number} l HSLᵤᵥ lightness (0..100).
* @param {number} [a] Alpha (0..1).
*/
exports.hsl = (h, s, l, a = 1) => {
let p = l + 16;
let i = p ** 3 / 1560896; // ← `sub1`
let j;
let x;
let y;
let z;
let m = i > 0.0088564516 ? i : l / 903.2962962; // ← `sub2`
let n;
let o = Infinity; // ← `c`

h = (h * PI) / 180; // ← `hrad`
exports.hsl = function(h, s, l, a) {
var p = l + 16;
var i = Math.pow(p, 3) / 1560896; // ← `sub1`
var j;
var x;
var y;
var z;
var m = i > 0.0088564516 ? i : l / 903.2962962; // ← `sub2`
var n;
var o = Infinity; // ← `c`

h = (h * Math.PI) / 180; // ← `hrad`

for (i = 0; i < 9; ) {
x = M[i++];
Expand All @@ -55,30 +58,32 @@ exports.hsl = (h, s, l, a = 1) => {
n =
((838422 * z + r * y + 731718 * x) * l * m - r * j * l) /
n /
(sin(h) - (((284517 * x - 94839 * z) * m) / n) * cos(h));
(Math.sin(h) - (((284517 * x - 94839 * z) * m) / n) * Math.cos(h));

o = n >= 0 ? min(o, (n / 100) * s) : o; // ← `c`
o = n >= 0 ? Math.min(o, (n / 100) * s) : o; // ← `c`
}
}

i = l * 13;
m = (cos(h) * o) / i + 0.19783000664283; // ← `varU`
n = (sin(h) * o) / i + 0.46831999493879; // ← `varV`
m = (Math.cos(h) * o) / i + 0.19783000664283; // ← `varU`
n = (Math.sin(h) * o) / i + 0.46831999493879; // ← `varV`

l = l <= 8 ? l / 903.2962962 : (p / 116) ** 3; // ← `y`
m = -(9 * l * m) / ((m - 4) * n - m * n); // ← `x`
o = normalize(a); // ← `rgba`
l = l <= 8 ? l / 903.2962962 : Math.pow(p / 116, 3); // ← `y`
m = -(9 * l * m) / ((m - 4) * n - m * n); // ← `x`
o = normalize(a == null ? 1 : a); // ← `rgba`

for (i = 0, j = 24; i < 9; j -= 8) {
o +=
(normalize(
(a = M[i++] * m + M[i++] * l + (M[i++] * (9 * l - 15 * n * l - n * m)) / (3 * n)) <= 0.0031308
? 12.92 * a
: 1.055 * a ** 0.416666666666666685 - 0.055,
: 1.055 * Math.pow(a, 0.416666666666666685) - 0.055
) <<
j) >>>
0;
}

return '#' + o.toString(16).padStart(8, 0);
o = o.toString(16);

return '#' + pad.slice(0, 8 - o.length) + o;
};
58 changes: 58 additions & 0 deletions test/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,68 @@ const { min, max, round, random } = Math;

const normalize = n => round(max(0, min(n, 1)) * 255);

const old = (h, s, l, a = 1) => {
let j;
let bottom;
let length;

const M = [
[3.240969941904521, -1.537383177570093, -0.498610760293],
[-0.96924363628087, 1.87596750150772, 0.041555057407175],
[0.055630079696993, -0.20397695888897, 1.056971514242878],
];

const fromLinear = c => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** 0.416666666666666685 - 0.055);

const { sin, cos, min } = Math;

const hrad = (h * Math.PI) / 180;
const bounds = [];
const sub1 = (l + 16) ** 3 / 1560896;
const sub2 = sub1 > 0.0088564516 ? sub1 : l / 903.2962962;

const _ = M.forEach(([a, b, c]) => {
for (j = 0; j < 2; j++) {
bottom = (632260 * c - 126452 * b) * sub2 + 126452 * j;

bounds.push([
((284517 * a - 94839 * c) * sub2) / bottom,
((838422 * c + 769860 * b + 731718 * a) * l * sub2 - 769860 * j * l) / bottom,
]);
}
});

const c = bounds.reduce((result, [a, b]) => {
length = b / (sin(hrad) - a * cos(hrad));

return length >= 0 ? min(result, (length / 100) * s) : result;
}, Infinity);

const varU = (cos(hrad) * c) / (13 * l) + 0.19783000664283;
const varV = (sin(hrad) * c) / (13 * l) + 0.46831999493879;

const y = l <= 8 ? l / 903.2962962 : ((l + 16) / 116) ** 3;
const x = -(9 * y * varU) / ((varU - 4) * varV - varU * varV);

return `rgba(${M.map(([a, b, c]) => {
return Math.round(
Math.max(0, min(fromLinear(a * x + b * y + (c * (9 * y - 15 * varV * y - varV * x)) / (3 * varV)), 1)) *
255,
);
})},${a})`;
};

// prettier-ignore
const FunA = (h, s, l, a) => (hsluvToHex([h, s, l]) + normalize(a).toString(16).padStart(2, 0));
const FunB = (h, s, l, a) => hsl(h, s, l, a);
const FunC = (h, s, l, a) => old(h, s, l, a);

let sum = [];

for (let i = 0; i < 10000; i++) {
sum.push(FunB(random(), random(), random(), random()));
sum.push(FunA(random(), random(), random(), random()));
sum.push(FunC(random(), random(), random(), random()));
}
sum = [];

Expand All @@ -29,3 +82,8 @@ a = performance.now();
for (let i = 0; i < r; i++) sum.push(FunB(random(), random(), random(), random()));
console.log('FunB : %s rps, side effect = %s.', (r / (performance.now() - a)) * 1000, sum.length);
sum = [];

a = performance.now();
for (let i = 0; i < r; i++) sum.push(FunC(random(), random(), random(), random()));
console.log('FunC : %s rps, side effect = %s.', (r / (performance.now() - a)) * 1000, sum.length);
sum = [];

0 comments on commit 27f9a77

Please sign in to comment.