@fils/color 0.0.9 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/canvas-utils.js +2 -1
- package/lib/utils.d.ts +14 -0
- package/lib/utils.js +63 -2
- package/package.json +5 -7
package/lib/canvas-utils.js
CHANGED
|
@@ -21,6 +21,7 @@ export function drawColorPickerBar(canvas, x, y, width, height) {
|
|
|
21
21
|
const h = height !== undefined ? height : canvas.height;
|
|
22
22
|
const ctx = canvas.getContext('2d');
|
|
23
23
|
for (let i = 0; i < w; i++) {
|
|
24
|
+
// const rad = (i * Math.PI) / 180;
|
|
24
25
|
const dx = _x + i;
|
|
25
26
|
const angle = 360 * i / w;
|
|
26
27
|
ctx.fillStyle = `hsl(${angle}, 100%, 50%)`;
|
|
@@ -42,7 +43,7 @@ export function drawColorPickerSL(canvas, angle, x, y, width, height) {
|
|
|
42
43
|
s: j,
|
|
43
44
|
b: i
|
|
44
45
|
});
|
|
45
|
-
ctx.fillStyle = hex;
|
|
46
|
+
ctx.fillStyle = hex; //`hsl(${angle}, ${j}%, ${100-i}%)`;
|
|
46
47
|
ctx.fillRect(_x + w * j / 100, _y + h - h * i / 100, sw, sh);
|
|
47
48
|
}
|
|
48
49
|
}
|
package/lib/utils.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export type HSLColor = {
|
|
|
13
13
|
s: number;
|
|
14
14
|
l: number;
|
|
15
15
|
};
|
|
16
|
+
export type HexColor = `#${string}`;
|
|
17
|
+
export declare function isValidHexColor(color: string): color is HexColor;
|
|
16
18
|
export declare function componentToHex(c: number): string;
|
|
17
19
|
export declare function rgbToHex(color: RGBColor): string;
|
|
18
20
|
export declare function hexToRgb(hex: string): RGBColor;
|
|
@@ -26,3 +28,15 @@ export declare function hsbToHex(color: HSBColor): string;
|
|
|
26
28
|
export declare function rgbToString(color: RGBColor): string;
|
|
27
29
|
export declare function hsbToString(color: HSBColor): string;
|
|
28
30
|
export declare function fixHex(color: string): string;
|
|
31
|
+
export type LABColor = {
|
|
32
|
+
l: number;
|
|
33
|
+
a: number;
|
|
34
|
+
b: number;
|
|
35
|
+
};
|
|
36
|
+
export declare function rgbToLab(color: RGBColor): LABColor;
|
|
37
|
+
export declare function colorDistance(color1: RGBColor, color2: RGBColor): number;
|
|
38
|
+
export declare function findClosestColor(target: RGBColor, palette: RGBColor[]): {
|
|
39
|
+
color: RGBColor;
|
|
40
|
+
distance: number;
|
|
41
|
+
index: number;
|
|
42
|
+
};
|
package/lib/utils.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export function isValidHexColor(color) {
|
|
2
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color);
|
|
3
|
+
}
|
|
1
4
|
export function componentToHex(c) {
|
|
2
5
|
const hex = c.toString(16);
|
|
3
6
|
return hex.length === 1 ? '0' + hex : hex;
|
|
@@ -22,7 +25,7 @@ export function rgbToHsl(color) {
|
|
|
22
25
|
const min = Math.min(r, g, b);
|
|
23
26
|
let h, s, l = (max + min) / 2;
|
|
24
27
|
if (max == min) {
|
|
25
|
-
h = s = 0;
|
|
28
|
+
h = s = 0; // achromatic
|
|
26
29
|
}
|
|
27
30
|
else {
|
|
28
31
|
let d = max - min;
|
|
@@ -63,9 +66,10 @@ export function hslToRgb(color) {
|
|
|
63
66
|
let r, g, b;
|
|
64
67
|
const h = color.h, s = color.s, l = color.l;
|
|
65
68
|
if (s == 0) {
|
|
66
|
-
r = g = b = l;
|
|
69
|
+
r = g = b = l; // achromatic
|
|
67
70
|
}
|
|
68
71
|
else {
|
|
72
|
+
// eslint-disable-next-line no-inner-declarations
|
|
69
73
|
let q = color.l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
70
74
|
let p = 2 * l - q;
|
|
71
75
|
r = hue2rgb(p, q, h + 1 / 3);
|
|
@@ -143,6 +147,7 @@ export function fixHex(color) {
|
|
|
143
147
|
while (fixedColor.length < 7) {
|
|
144
148
|
fixedColor += "F";
|
|
145
149
|
}
|
|
150
|
+
// Replace invalid characters with their closest valid hexadecimal equivalent using a regular expression
|
|
146
151
|
fixedColor = fixedColor.replace(/[^A-F0-9]/g, (c) => {
|
|
147
152
|
switch (c) {
|
|
148
153
|
case "#":
|
|
@@ -174,3 +179,59 @@ export function fixHex(color) {
|
|
|
174
179
|
});
|
|
175
180
|
return fixedColor;
|
|
176
181
|
}
|
|
182
|
+
// RGB to LAB conversion (via XYZ)
|
|
183
|
+
export function rgbToLab(color) {
|
|
184
|
+
// Convert RGB to linear RGB
|
|
185
|
+
let r = color.r / 255;
|
|
186
|
+
let g = color.g / 255;
|
|
187
|
+
let b = color.b / 255;
|
|
188
|
+
// Apply sRGB gamma correction
|
|
189
|
+
r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
|
|
190
|
+
g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
|
|
191
|
+
b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
|
|
192
|
+
// Convert to XYZ using sRGB matrix (D65 illuminant)
|
|
193
|
+
let x = (r * 0.4124564 + g * 0.3575761 + b * 0.1804375) * 100;
|
|
194
|
+
let y = (r * 0.2126729 + g * 0.7151522 + b * 0.0721750) * 100;
|
|
195
|
+
let z = (r * 0.0193339 + g * 0.1191920 + b * 0.9503041) * 100;
|
|
196
|
+
// Normalize for D65 white point
|
|
197
|
+
x /= 95.047;
|
|
198
|
+
y /= 100.000;
|
|
199
|
+
z /= 108.883;
|
|
200
|
+
// Convert XYZ to LAB
|
|
201
|
+
x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x + 16 / 116);
|
|
202
|
+
y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y + 16 / 116);
|
|
203
|
+
z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z + 16 / 116);
|
|
204
|
+
return {
|
|
205
|
+
l: (116 * y) - 16,
|
|
206
|
+
a: 500 * (x - y),
|
|
207
|
+
b: 200 * (y - z)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// Simple ΔE*ab color difference (good enough for most cases)
|
|
211
|
+
export function colorDistance(color1, color2) {
|
|
212
|
+
const lab1 = rgbToLab(color1);
|
|
213
|
+
const lab2 = rgbToLab(color2);
|
|
214
|
+
const deltaL = lab1.l - lab2.l;
|
|
215
|
+
const deltaA = lab1.a - lab2.a;
|
|
216
|
+
const deltaB = lab1.b - lab2.b;
|
|
217
|
+
return Math.sqrt(deltaL * deltaL + deltaA * deltaA + deltaB * deltaB);
|
|
218
|
+
}
|
|
219
|
+
// Find closest color in palette
|
|
220
|
+
export function findClosestColor(target, palette) {
|
|
221
|
+
let minDistance = Infinity;
|
|
222
|
+
let closestColor = palette[0];
|
|
223
|
+
let closestIndex = 0;
|
|
224
|
+
palette.forEach((paletteColor, index) => {
|
|
225
|
+
const distance = colorDistance(target, paletteColor);
|
|
226
|
+
if (distance < minDistance) {
|
|
227
|
+
minDistance = distance;
|
|
228
|
+
closestColor = paletteColor;
|
|
229
|
+
closestIndex = index;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
color: closestColor,
|
|
234
|
+
distance: minDistance,
|
|
235
|
+
index: closestIndex
|
|
236
|
+
};
|
|
237
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fils/color",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Color Package written in TypeScript",
|
|
5
5
|
"main": "lib/main.js",
|
|
6
6
|
"repository": "git@github.com:fil-studio/fils.git",
|
|
@@ -9,15 +9,13 @@
|
|
|
9
9
|
"license": "Apache-2.0",
|
|
10
10
|
"private": false,
|
|
11
11
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"build": "tsc
|
|
12
|
+
"prepublishOnly": "yarn build",
|
|
13
|
+
"build": "tsc"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"lib"
|
|
17
|
-
"examples",
|
|
18
|
-
"README.md"
|
|
16
|
+
"lib"
|
|
19
17
|
],
|
|
20
18
|
"devDependencies": {
|
|
21
|
-
"typescript": "^
|
|
19
|
+
"typescript": "^5.9.3"
|
|
22
20
|
}
|
|
23
21
|
}
|