增加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