-
Notifications
You must be signed in to change notification settings - Fork 0
/
color-transformer.ts
146 lines (140 loc) · 4.72 KB
/
color-transformer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { hsl, HSLColor, rgb, RGBColor } from "d3-color";
/**
* If the value is a numeric or a purely numeric character, the associated parameter uses the value as the absolute value
*
* If the value is a character that starts with '+' or '-', the relative value is computed with the parameters associated with the base color
*/
export type ColorTransformValue = number | string;
/**
* Use for calc next color-stop with common color
*
* @export
* @interface GradientHint
*/
export interface ColorTransformTarget {
/**
* By default, if there is no color with a 0% stop, the first color declared will be at that point.
* Similarly, the last color will continue to the 100% mark,
* or be at the 100% mark if no length has been declared on that last stop.
*
* @type {string} from 0%-100%
* @memberof ColorTransformTarget
*/
markPercent?: string;
opacity?: ColorTransformValue;
/**
* Use for calc next color stop with hsl
*
* @type {{
* h?: ColorTransformValue; // from 0-360
* s?: ColorTransformValue; // from 0-1
* l?: ColorTransformValue; // from 0-1
* }}
* @memberof ColorTransformTarget
*/
hslTransformValue?: {
h?: ColorTransformValue;
s?: ColorTransformValue;
l?: ColorTransformValue;
};
/**
* Use for calc next color stop with rgb
*
* Priority lower than hslTransformValue
*
* @type {{
* r?: ColorTransformValue; // from 0-255
* g?: ColorTransformValue; // from 0-255
* a?: ColorTransformValue; // from 0-255
* }}
* @memberof ColorTransformTarget
*/
rgbTransformValue?: {
r?: ColorTransformValue;
g?: ColorTransformValue;
b?: ColorTransformValue;
};
/**
* Provide a function for specified transform color;
*
* Priority lower than hslTransformValue and rgbTransformValue
*/
transformFn?: (baseColor: HSLColor, transformTarget: HSLColor) => undefined | HSLColor;
}
export type ColorTransformTargets = ColorTransformTarget[];
export function transformColor(
baseColor: string,
transformTarget: ColorTransformTarget
): HSLColor {
const transform = (source: number, to: ColorTransformValue | undefined, min?: number, max?: number) => {
let target: number | undefined;
if (typeof to === "string") {
const transformValue = Number(to);
if(Number.isFinite(transformValue)) {
target = to.startsWith("+") || to.startsWith("-") ? source + transformValue : transformValue;
}
} else if (typeof to === "number") {
target = to;
}
if (target === undefined) {
target = source;
}
if (min !== undefined && target < min) {
return min;
} else if (max !== undefined && target > max) {
return max;
} else {
return target;
}
};
let color: HSLColor = hsl(baseColor);
if (!Number.isFinite(color.h)) {
// handle transfrom hsl exception - d3-color BUG
const rgbaExp = /^(rgb)a?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*%?,\s*(\d{1,3})\s*%?(?:,\s*(\d+(?:\.\d+)?)\s*)?\)$/i
const hexExp = /^#([0-9a-fA-F]{6})([0-9]{1,2})$/;
if (rgbaExp.test(baseColor)) {
const rgbaMatch = baseColor.match(rgbaExp);
color = hsl(`rgba(${rgbaMatch![2].trim()}, ${rgbaMatch![3].trim()}, ${rgbaMatch![4].trim()}, 1)`);
color.opacity = Number(rgbaMatch![5]);
} else if (hexExp.test(baseColor)) {
const hexMatch = baseColor.match(hexExp);
color = hsl(`#${hexMatch![1]}`);
color.opacity = Number((Number(hexMatch![2]) / 100).toFixed(2));
}
if (Number.isNaN(color.h)) {
color.h = 0;
}
if (Number.isNaN(color.s)) {
color.s = color.h
}
}
if (transformTarget.hslTransformValue) {
const hslColor = color;
color = hsl(
transform(hslColor.h, transformTarget.hslTransformValue.h, 0, 360),
transform(hslColor.s, transformTarget.hslTransformValue.s, 0, 1),
transform(hslColor.l, transformTarget.hslTransformValue.l, 0, 1),
color.opacity
);
}
if (transformTarget.rgbTransformValue) {
const rgbColor = rgb(color || baseColor);
color = hsl(rgb(
transform(rgbColor.r, transformTarget.rgbTransformValue.r, 0, 255),
transform(rgbColor.g, transformTarget.rgbTransformValue.g, 0, 255),
transform(rgbColor.b, transformTarget.rgbTransformValue.b, 0, 255),
color.opacity
));
}
if (transformTarget.opacity !== undefined) {
color.opacity = transform(color.opacity, transformTarget.opacity, 0, 1);
}
if (transformTarget.transformFn) {
color = transformTarget.transformFn(hsl(baseColor), color) || color;
}
if (transformTarget.markPercent) {
const hslToString = color.toString.bind(color);
color.toString = () => `${hslToString()} ${transformTarget.markPercent}`;
}
return color;
}