增加HSL输入功能。
This commit is contained in:
parent
67f220facb
commit
cac57726ff
21
src/components/ColorComponentInput.module.css
Normal file
21
src/components/ColorComponentInput.module.css
Normal file
|
@ -0,0 +1,21 @@
|
|||
@layer components {
|
||||
.rgb_input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-m);
|
||||
.rgb_input {
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.component_row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
.component_input {
|
||||
flex: 1 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
234
src/components/ColorComponentInput.tsx
Normal file
234
src/components/ColorComponentInput.tsx
Normal file
|
@ -0,0 +1,234 @@
|
|||
import { Icon } from '@iconify/react/dist/iconify.js';
|
||||
import cx from 'clsx';
|
||||
import { clamp, divide, floor, times, trim } from 'lodash-es';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { useColorFunction } from '../ColorFunctionContext';
|
||||
import styles from './ColorComponentInput.module.css';
|
||||
|
||||
type ColorComponentInputProps = {
|
||||
color: string | null;
|
||||
onChange: (color: string) => void;
|
||||
};
|
||||
|
||||
export function ColorComponentInput({ color, onChange }: ColorComponentInputProps) {
|
||||
const { colorFn } = useColorFunction();
|
||||
const [hex, setHex] = useState<string>(color ?? '000000');
|
||||
const [r, setR] = useState<number>(0);
|
||||
const [g, setG] = useState<number>(0);
|
||||
const [b, setB] = useState<number>(0);
|
||||
const [h, setH] = useState<number>(0);
|
||||
const [s, setS] = useState<number>(0);
|
||||
const [l, setL] = useState<number>(0);
|
||||
const updateHex = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
try {
|
||||
const hexValue = trim(evt.target.value, '#');
|
||||
const [r, g, b] = colorFn?.represent_rgb(hexValue) ?? [0, 0, 0];
|
||||
setR(r);
|
||||
setG(g);
|
||||
setB(b);
|
||||
const [h, s, l] = colorFn?.represent_hsl(hexValue) ?? [0, 0, 0];
|
||||
setH(h);
|
||||
setS(s);
|
||||
setL(l);
|
||||
setHex(hexValue);
|
||||
onChange(hexValue);
|
||||
} catch (e) {
|
||||
console.error('[Convert HEX]', e);
|
||||
}
|
||||
};
|
||||
const updateRGB = (colorValue: string) => {
|
||||
try {
|
||||
const [r, g, b] = colorFn?.represent_rgb(colorValue) ?? [0, 0, 0];
|
||||
setR(r);
|
||||
setG(g);
|
||||
setB(b);
|
||||
} catch (e) {
|
||||
console.error('[Convert RGB]', e);
|
||||
}
|
||||
};
|
||||
const updateHSL = (colorValue: string) => {
|
||||
try {
|
||||
const [h, s, l] = colorFn?.represent_hsl(colorValue) ?? [0, 0, 0];
|
||||
setH(floor(h, 1));
|
||||
setS(floor(times(s, 100), 2));
|
||||
setL(floor(times(l, 100), 2));
|
||||
} catch (e) {
|
||||
console.error('[Convert HSL]', e);
|
||||
}
|
||||
};
|
||||
const updateR = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = clamp(parseInt(evt.target.value, 10), 0, 255);
|
||||
try {
|
||||
const cHex = colorFn?.rgb_to_hex(value, g, b);
|
||||
setR(value);
|
||||
setHex(cHex);
|
||||
updateHSL(cHex);
|
||||
onChange(cHex);
|
||||
} catch (e) {
|
||||
console.error('[Update R]', e, value);
|
||||
}
|
||||
};
|
||||
const updateG = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = clamp(parseInt(evt.target.value, 10), 0, 255);
|
||||
try {
|
||||
const cHex = colorFn?.rgb_to_hex(r, value, b);
|
||||
setG(value);
|
||||
setHex(cHex);
|
||||
updateHSL(cHex);
|
||||
onChange(cHex);
|
||||
} catch (e) {
|
||||
console.error('[Update G]', e, value);
|
||||
}
|
||||
};
|
||||
const updateB = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = clamp(parseInt(evt.target.value, 10), 0, 255);
|
||||
try {
|
||||
const cHex = colorFn?.rgb_to_hex(r, g, value);
|
||||
setB(value);
|
||||
setHex(cHex);
|
||||
updateHSL(cHex);
|
||||
onChange(cHex);
|
||||
} catch (e) {
|
||||
console.error('[Update B]', e, value);
|
||||
}
|
||||
};
|
||||
const updateH = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = parseInt(evt.target.value, 10);
|
||||
if (value > 360) {
|
||||
value -= 360;
|
||||
}
|
||||
if (value < 0) {
|
||||
value += 360;
|
||||
}
|
||||
try {
|
||||
const cHex = colorFn.hsl_to_hex(value, divide(s, 100), divide(l, 100));
|
||||
setH(value);
|
||||
setHex(cHex);
|
||||
updateRGB(cHex);
|
||||
onChange(cHex);
|
||||
} catch (e) {
|
||||
console.error('[Update Hue]', e, value);
|
||||
}
|
||||
};
|
||||
const updateS = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = clamp(parseInt(evt.target.value, 10), 0, 100);
|
||||
try {
|
||||
const cHex = colorFn.hsl_to_hex(h, divide(value, 100), divide(l, 100));
|
||||
setS(value);
|
||||
setHex(cHex);
|
||||
updateRGB(cHex);
|
||||
onChange(cHex);
|
||||
} catch (e) {
|
||||
console.error('[Update Saturation]', e, value);
|
||||
}
|
||||
};
|
||||
const updateL = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = clamp(parseInt(evt.target.value, 10), 0, 100);
|
||||
try {
|
||||
const cHex = colorFn.hsl_to_hex(h, divide(s, 100), divide(value, 100));
|
||||
setL(value);
|
||||
setHex(cHex);
|
||||
updateRGB(cHex);
|
||||
onChange(cHex);
|
||||
} catch (e) {
|
||||
console.error('[Update Lightness]', e, value);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const [r, g, b] = colorFn?.represent_rgb(color) ?? [0, 0, 0];
|
||||
setR(r);
|
||||
setG(g);
|
||||
setB(b);
|
||||
const [h, s, l] = colorFn?.represent_hsl(color) ?? [0, 0, 0];
|
||||
setH(floor(h, 1));
|
||||
setS(floor(times(s, 100), 2));
|
||||
setL(floor(times(l, 100), 2));
|
||||
} catch (e) {
|
||||
console.error('[Convert RGB]', e);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.rgb_input}>
|
||||
<div className={cx('input_wrapper')}>
|
||||
<Icon icon="tabler:hash" />
|
||||
<input type="text" value={hex} onChange={updateHex} className={styles.rgb_input} />
|
||||
</div>
|
||||
<div className={styles.component_row}>
|
||||
<div className={cx('input_wrapper', styles.component_input)}>
|
||||
<Icon icon="tabler:letter-r" />
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={255}
|
||||
value={r}
|
||||
onChange={updateR}
|
||||
className={styles.rgb_input}
|
||||
/>
|
||||
</div>
|
||||
<div className={cx('input_wrapper', styles.component_input)}>
|
||||
<Icon icon="tabler:letter-g" />
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={255}
|
||||
value={g}
|
||||
onChange={updateG}
|
||||
className={styles.rgb_input}
|
||||
/>
|
||||
</div>
|
||||
<div className={cx('input_wrapper', styles.component_input)}>
|
||||
<Icon icon="tabler:letter-b" />
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={255}
|
||||
value={b}
|
||||
onChange={updateB}
|
||||
className={styles.rgb_input}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.component_row}>
|
||||
<div className={cx('input_wrapper', styles.component_input)}>
|
||||
<Icon icon="tabler:letter-h" />
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={360}
|
||||
value={h}
|
||||
onChange={updateH}
|
||||
className={styles.rgb_input}
|
||||
/>
|
||||
<span>°</span>
|
||||
</div>
|
||||
<div className={cx('input_wrapper', styles.component_input)}>
|
||||
<Icon icon="tabler:letter-s" />
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
value={s}
|
||||
onChange={updateS}
|
||||
className={styles.rgb_input}
|
||||
/>
|
||||
<Icon icon="tabler:percentage" />
|
||||
</div>
|
||||
<div className={cx('input_wrapper', styles.component_input)}>
|
||||
<Icon icon="tabler:letter-l" />
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
value={l}
|
||||
onChange={updateL}
|
||||
className={styles.rgb_input}
|
||||
/>
|
||||
<Icon icon="tabler:percentage" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user