darkmode
This commit is contained in:
@@ -1,102 +0,0 @@
|
||||
export default [
|
||||
'#800080',
|
||||
'#800083',
|
||||
'#800087',
|
||||
'#7f008a',
|
||||
'#7f008d',
|
||||
'#7e0090',
|
||||
'#7d0094',
|
||||
'#7c0097',
|
||||
'#7a009a',
|
||||
'#79009d',
|
||||
'#7700a1',
|
||||
'#7600a4',
|
||||
'#7400a7',
|
||||
'#7200aa',
|
||||
'#6f00ae',
|
||||
'#6d00b1',
|
||||
'#6a00b4',
|
||||
'#6700b7',
|
||||
'#6400bb',
|
||||
'#6100be',
|
||||
'#5e00c1',
|
||||
'#5b00c4',
|
||||
'#5700c8',
|
||||
'#5300cb',
|
||||
'#4f00ce',
|
||||
'#4b00d1',
|
||||
'#4700d5',
|
||||
'#4200d8',
|
||||
'#3e00db',
|
||||
'#3900de',
|
||||
'#3400e2',
|
||||
'#2f00e5',
|
||||
'#2a00e8',
|
||||
'#2400eb',
|
||||
'#1f00ef',
|
||||
'#1900f2',
|
||||
'#1300f5',
|
||||
'#0d00f8',
|
||||
'#0600fc',
|
||||
'#0000ff',
|
||||
'#0000ff',
|
||||
'#0021f7',
|
||||
'#003fee',
|
||||
'#005ce6',
|
||||
'#0076dd',
|
||||
'#008ed5',
|
||||
'#00a3cc',
|
||||
'#00b7c4',
|
||||
'#00bbaf',
|
||||
'#00b38f',
|
||||
'#00aa72',
|
||||
'#00a256',
|
||||
'#00993d',
|
||||
'#009127',
|
||||
'#008812',
|
||||
'#008000',
|
||||
'#008000',
|
||||
'#118c00',
|
||||
'#259700',
|
||||
'#3ca300',
|
||||
'#56ae00',
|
||||
'#72ba00',
|
||||
'#92c500',
|
||||
'#b4d100',
|
||||
'#d9dc00',
|
||||
'#e8cf00',
|
||||
'#f3bb00',
|
||||
'#ffa500',
|
||||
'#ffa500',
|
||||
'#ff9800',
|
||||
'#ff8c00',
|
||||
'#ff7f00',
|
||||
'#ff7200',
|
||||
'#ff6600',
|
||||
'#ff5900',
|
||||
'#ff4c00',
|
||||
'#ff3f00',
|
||||
'#ff3300',
|
||||
'#ff2600',
|
||||
'#ff1900',
|
||||
'#ff0d00',
|
||||
'#ff0000',
|
||||
'#ff0000',
|
||||
'#f8000f',
|
||||
'#f0001c',
|
||||
'#e90029',
|
||||
'#e10035',
|
||||
'#da0040',
|
||||
'#d2004a',
|
||||
'#cb0053',
|
||||
'#c3005c',
|
||||
'#bc0063',
|
||||
'#b4006a',
|
||||
'#ad0070',
|
||||
'#a50075',
|
||||
'#9e0079',
|
||||
'#96007c',
|
||||
'#8f007e',
|
||||
'#870080',
|
||||
'#800080'
|
||||
];
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Temperature color scale ported from the open-meteo/weather-map-layer
|
||||
* project (src/utils/color-scales.ts) so tables and maps share the same
|
||||
* color language. Values between breakpoints are linearly interpolated.
|
||||
*/
|
||||
|
||||
export type RGBA = [number, number, number, number];
|
||||
|
||||
export interface BreakpointScale {
|
||||
unit: string;
|
||||
breakpoints: number[];
|
||||
colors: RGBA[];
|
||||
}
|
||||
|
||||
export const temperatureScale: BreakpointScale = {
|
||||
unit: '°C',
|
||||
breakpoints: [
|
||||
-80, -65, -50, -40, -32, -28, -24, -20, -17.5, -15, -12.5, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8,
|
||||
10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50
|
||||
],
|
||||
colors: [
|
||||
[74, 13, 0, 1],
|
||||
[130, 1, 29, 1],
|
||||
[185, 4, 114, 1],
|
||||
[221, 6, 193, 1],
|
||||
[207, 6, 241, 1],
|
||||
[163, 5, 243, 1],
|
||||
[118, 4, 246, 1],
|
||||
[71, 3, 249, 1],
|
||||
[41, 2, 250, 1],
|
||||
[11, 1, 252, 1],
|
||||
[1, 22, 253, 1],
|
||||
[0, 52, 255, 1],
|
||||
[35, 99, 251, 1],
|
||||
[69, 139, 247, 1],
|
||||
[102, 171, 245, 1],
|
||||
[134, 197, 245, 1],
|
||||
[114, 232, 165, 1],
|
||||
[78, 232, 133, 1],
|
||||
[40, 233, 96, 1],
|
||||
[17, 220, 61, 1],
|
||||
[9, 191, 36, 1],
|
||||
[4, 160, 15, 1],
|
||||
[0, 128, 0, 1],
|
||||
[40, 160, 0, 1],
|
||||
[96, 192, 0, 1],
|
||||
[167, 223, 0, 1],
|
||||
[255, 255, 0, 1],
|
||||
[255, 237, 0, 1],
|
||||
[255, 219, 0, 1],
|
||||
[255, 201, 0, 1],
|
||||
[255, 183, 0, 1],
|
||||
[255, 165, 0, 1],
|
||||
[255, 138, 0, 1],
|
||||
[255, 110, 0, 1],
|
||||
[255, 83, 0, 1],
|
||||
[255, 55, 0, 1],
|
||||
[255, 27, 0, 1],
|
||||
[255, 0, 0, 1],
|
||||
[228, 0, 10, 1],
|
||||
[201, 0, 18, 1],
|
||||
[174, 0, 23, 1],
|
||||
[147, 0, 26, 1]
|
||||
]
|
||||
};
|
||||
|
||||
const lerp = (a: number, b: number, t: number): number => a + (b - a) * t;
|
||||
|
||||
/** Linearly interpolated color for `value` on a breakpoint scale. */
|
||||
export const sampleScale = (breakpoints: number[], colors: RGBA[], value: number): RGBA => {
|
||||
if (!Number.isFinite(value) || value <= breakpoints[0]) return colors[0];
|
||||
const last = breakpoints.length - 1;
|
||||
if (value >= breakpoints[last]) return colors[last];
|
||||
let i = 0;
|
||||
while (value > breakpoints[i + 1]) i++;
|
||||
const t = (value - breakpoints[i]) / (breakpoints[i + 1] - breakpoints[i]);
|
||||
const [r1, g1, b1, a1] = colors[i];
|
||||
const [r2, g2, b2, a2] = colors[i + 1];
|
||||
return [
|
||||
Math.round(lerp(r1, r2, t)),
|
||||
Math.round(lerp(g1, g2, t)),
|
||||
Math.round(lerp(b1, b2, t)),
|
||||
lerp(a1, a2, t)
|
||||
];
|
||||
};
|
||||
|
||||
export const rgbaCss = ([r, g, b, a]: RGBA): string =>
|
||||
`rgba(${r}, ${g}, ${b}, ${Math.round(a * 1000) / 1000})`;
|
||||
|
||||
/**
|
||||
* Whether text over `color` should be white, after alpha-compositing the
|
||||
* color onto the page background (light or dark).
|
||||
*/
|
||||
export const needsWhiteText = ([r, g, b, a]: RGBA, dark = false): boolean => {
|
||||
const base = dark ? 26 : 255;
|
||||
const cr = r * a + base * (1 - a);
|
||||
const cg = g * a + base * (1 - a);
|
||||
const cb = b * a + base * (1 - a);
|
||||
return cr * 0.299 + cg * 0.587 + cb * 0.114 <= 150;
|
||||
};
|
||||
@@ -1,57 +1,13 @@
|
||||
import colorScaleHex from './color-scale-hex';
|
||||
import { type RGBA, needsWhiteText, rgbaCss, sampleScale, temperatureScale } from './color-scales';
|
||||
|
||||
const componentFromStr = (numStr: string, percent: number) => {
|
||||
const num = Math.max(0, parseInt(numStr, 10));
|
||||
return percent ? Math.floor((255 * Math.min(100, num)) / 100) : Math.min(255, num);
|
||||
};
|
||||
const toCelsius = (temperature: number, unit: string): number =>
|
||||
unit === 'celsius' ? temperature : ((temperature - 32) * 5) / 9;
|
||||
|
||||
export const rgbToHex = (rgb: string): string => {
|
||||
const rgbRegex = /^rgb\(\s*(-?\d+)(%?)\s*,\s*(-?\d+)(%?)\s*,\s*(-?\d+)(%?)\s*\)$/;
|
||||
let result,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
hex = '';
|
||||
if ((result = rgbRegex.exec(rgb))) {
|
||||
r = componentFromStr(result[1], Number(result[2]));
|
||||
g = componentFromStr(result[3], Number(result[4]));
|
||||
b = componentFromStr(result[5], Number(result[6]));
|
||||
export const getTempColor = (temperature: number, unit = 'celsius'): RGBA =>
|
||||
sampleScale(temperatureScale.breakpoints, temperatureScale.colors, toCelsius(temperature, unit));
|
||||
|
||||
hex = (0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
}
|
||||
if (!rgb) {
|
||||
return '355522';
|
||||
}
|
||||
return hex;
|
||||
};
|
||||
|
||||
export const hexToRgb = (hex: string): [number, number, number] => {
|
||||
const h = hex.replace('#', '');
|
||||
return [
|
||||
parseInt(h.substring(0, 2), 16),
|
||||
parseInt(h.substring(2, 4), 16),
|
||||
parseInt(h.substring(4, 6), 16)
|
||||
];
|
||||
};
|
||||
|
||||
export const getColor = (temperature: number, unit = 'celsius'): string => {
|
||||
if (unit !== 'celsius') {
|
||||
temperature = Math.round(((temperature - 32) * 5) / 9);
|
||||
}
|
||||
|
||||
let index: number;
|
||||
if (temperature <= -40) {
|
||||
index = 0;
|
||||
} else if (temperature >= 60) {
|
||||
index = colorScaleHex.length - 1;
|
||||
} else {
|
||||
// clamp: the scale has exactly 100 entries (-45..54), temperatures in
|
||||
// [55, 60) would otherwise index past the end
|
||||
index = Math.min(colorScaleHex.length - 1, Math.max(0, Math.round(temperature) + 45));
|
||||
}
|
||||
|
||||
return colorScaleHex[index];
|
||||
};
|
||||
export const getColor = (temperature: number, unit = 'celsius'): string =>
|
||||
rgbaCss(getTempColor(temperature, unit));
|
||||
|
||||
export interface TempStyle {
|
||||
bg: string;
|
||||
@@ -59,24 +15,6 @@ export interface TempStyle {
|
||||
}
|
||||
|
||||
export const getTempStyle = (temp: number, unit: string): TempStyle => {
|
||||
const bg = getColor(temp, unit);
|
||||
const fg = textWhite(hexToRgb(bg)) ? 'white' : 'black';
|
||||
return { bg, fg };
|
||||
};
|
||||
|
||||
export const textWhite = (
|
||||
[r, g, b, a]: [number, number, number, number] | [number, number, number],
|
||||
dark?: boolean,
|
||||
globalOpacity?: number
|
||||
): boolean => {
|
||||
const alpha = ((a || 1) * (globalOpacity || 100)) / 100;
|
||||
if (alpha < 0.65) {
|
||||
if (dark) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check luminance
|
||||
return r * 0.299 + g * 0.587 + b * 0.114 <= 150;
|
||||
const rgba = getTempColor(temp, unit);
|
||||
return { bg: rgbaCss(rgba), fg: needsWhiteText(rgba) ? 'white' : 'black' };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user