| 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 | 1x
1x
1x
10x
10x
20x
20x
20x
10x
10x
10x
10x
10x
1x
5x
5x
5x
5x
5x
5x
5x
| import Chroma from "chroma-js";
/**
* Interface that describes the configuration options of the color algorithm .
*/
export interface ColorConfig {
/**
* The number of colors to generate
*/
count: number;
/**
* The amount to pad light values
*/
paddingLight: number;
/**
* The amount to pad dark values
*/
paddingDark: number;
/**
* Saturation of the light values
*/
saturationLight: number;
/**
* Saturation of the dark values
*/
saturationDark: number;
/**
* Brightness of light values
*/
brightnessLight: number;
/**
* Brightness of dark values
*/
brightnessDark: number;
/**
* The overlay filter on light values
*/
filterOverlayLight: number;
/**
* The overlay filter on dark values
*/
filterOverlayDark: number;
/**
* The multiply filter on light values
*/
filterMultiplyLight: number;
/**
* The multiply filter on dark values
*/
filterMultiplyDark: number;
}
/**
* A color value as a string - can be hex or RGB
*/
export type Color = string;
/**
* A function that applies a filter to a color
*/
export type FilterFunction = (
background: Color,
foreground: Color,
amount: number
) => Chroma;
const white: Color = "#FFF";
const black: Color = "#000";
/**
* Saturates a color by a given value. If the value is lower than the lowpass it will not make an adjustment
*/
function saturate(
color: Chroma,
referenceColor: Color,
value: number,
lowpass: number = 0.05,
highpass: number = 1
): Chroma {
const saturation: number = Chroma(referenceColor).get("hsl.s");
return saturation >= lowpass && saturation <= highpass
? color.saturate(value)
: color;
}
/**
* Creates a filter function by the name of the filter
*/
function filter(name: string): FilterFunction {
return (background: Color, foreground: Color, value: number): Chroma => {
const adjustment: Chroma = Chroma.blend(background, foreground, name);
return Chroma.mix(foreground, adjustment, value, "rgb");
};
}
/**
* Adjusts the threshold of a color-range based on configuration. Applies various color transformations and returns
* the new color threshold. This function is used to adjust the extremes of a color range.
*/
function adjustThreshold(
thresholdColor: Color,
referenceColor: Color,
saturation: number,
brightness: number,
multiply: number,
overlay: number
): Chroma {
const color: any = Chroma(thresholdColor);
const saturated: Chroma = saturate(color, referenceColor, saturation);
const brightened: Chroma = saturated.brighten(brightness);
const multiplied: Chroma = filter("multiply")(referenceColor, brightened, multiply);
return filter("overlay")(referenceColor, multiplied, overlay);
}
/**
* Algorithm to generate a range of color variants based on a single color, where the input color is the middle
* of the returned color range.
*/
export function range(color: Color, options: Partial<ColorConfig> = {}): Color[] {
color = Chroma(color).hex("rgb");
const defaults: ColorConfig = {
count: 7,
paddingLight: 0.185,
paddingDark: 0.16,
saturationLight: 0.35,
saturationDark: 1.25,
brightnessLight: 0,
brightnessDark: 0,
filterOverlayLight: 0,
filterOverlayDark: 0.25,
filterMultiplyLight: 0,
filterMultiplyDark: 0,
};
const normalizedOptions: ColorConfig = Object.assign({}, defaults, options);
// Create the color-range to derive the color variants from
const colorRange: Color[] = Chroma.scale([white, color, black])
.padding([normalizedOptions.paddingLight, normalizedOptions.paddingDark])
.colors(3);
const lightest: Color = adjustThreshold(
colorRange[0],
color,
normalizedOptions.saturationLight,
normalizedOptions.brightnessLight,
normalizedOptions.filterMultiplyLight,
normalizedOptions.filterOverlayLight
);
const darkest: Color = adjustThreshold(
colorRange[2],
color,
normalizedOptions.saturationDark,
normalizedOptions.brightnessDark,
normalizedOptions.filterMultiplyDark,
normalizedOptions.filterOverlayDark
);
return Chroma.scale([lightest, color, darkest]).colors(normalizedOptions.count);
}
|