<div id="app"></div>
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-size: 16px;
line-height: 1.4;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
}
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
&--dark {
color: white;
}
}
.color-range {
input {
display: block;
}
}
.color-input {
display: flex;
width: 130px;
margin-bottom: .5rem;
&--value {
margin-left: auto;
}
}
.hex {
margin-top: 1rem;
}
.wrap {
display: flex;
justify-content: center;
}
.box {
margin: 10px;
}
View Compiled
const { Fragment, useState } = React;
const rgbToLightness = (r,g,b) => {
const max = Math.max(r,g,b);
const min = Math.min(r,g,b);
return 1/2 * (max + min);
}
const rgbToSaturation = (r,g,b) => {
const max = Math.max(r,g,b);
const min = Math.min(r,g,b);
const l = rgbToLightness(r,g,b);
return (l === 0 || l === 1) ? 0 : (max - min)/(1 - Math.abs(2 * l - 1));
}
const rgbToHue = (r,g,b) => {
let hue = Math.round(
Math.atan2(
Math.sqrt(3) * (g - b),
2 * r - g - b,
) * 180 / Math.PI
);
while (hue < 0) {
hue = hue + 360;
}
return hue;
}
const rgbToHsl = (r,g,b) => {
const hue = rgbToHue(r,g,b);
const saturation = rgbToSaturation(r,g,b);
const lightness = rgbToLightness(r,g,b);
return [hue, saturation, lightness];
}
const hslToRgb = (h,s,l) => {
const C = (1 - Math.abs(2 * l - 1)) * s;
const hPrime = h / 60;
const X = C * (1 - Math.abs(hPrime % 2 - 1));
const m = l - C/2;
const withLight = (r,g,b) => [r+m, g+m, b+m];
if (hPrime <= 1) { return withLight(C,X,0); } else
if (hPrime <= 2) { return withLight(X,C,0); } else
if (hPrime <= 3) { return withLight(0,C,X); } else
if (hPrime <= 4) { return withLight(0,X,C); } else
if (hPrime <= 5) { return withLight(X,0,C); } else
if (hPrime <= 6) { return withLight(C,0,X); }
}
const ValueRange = ({setColor, setOpposing, currentValue, name, label, min=0, max=100, ...rest}) => {
const onChange = e => {
setColor(e.target.value);
setOpposing({...rest, ...{[name]: e.target.value}});
}
return (
<Fragment>
<label className="color-range">
<input
type="range"
value={currentValue}
min={min}
max={max}
onChange={onChange}
/>
</label>
<label className="color-input">
{label}:
<input
className="color-input--value"
type="number"
min="0" max={max}
value={currentValue}
onChange={onChange}
/>
</label>
</Fragment>
)
}
const App = () => {
const [hue, setHue] = useState(0);
const [saturation, setSaturation] = useState(100);
const [lightness, setLightness] = useState(50);
const [red, setRed] = useState(255);
const [green, setGreen] = useState(0);
const [blue, setBlue] = useState(0);
const setRgb = (attributes) => {
const [r,g,b] = hslToRgb(attributes.hue, attributes.saturation / 100, attributes.lightness / 100);
setRed(Math.round(r * 255));
setGreen(Math.round(g * 255));
setBlue(Math.round(b * 255));
}
const setHsl = (attributes) => {
const [h,s,l] = rgbToHsl(attributes.red / 255,attributes.green / 255,attributes.blue / 255);
setHue(Math.round(h || 0));
setSaturation(Math.round(s * 100));
setLightness(Math.round(l * 100));
}
const hsl = {hue, saturation, lightness};
const rgb = {red, green, blue};
const attributes = {...hsl,...rgb};
const color = `hsl(${hue}deg,${saturation}%,${lightness}%)`;
return (
<div className={`app app--${lightness < 40 ? 'dark' : 'light'}`} style={{backgroundColor: color}}>
<h1>颜色模式之间转换</h1>
<div class="wrap">
<div class="box">
<h2>HSL</h2>
<ValueRange {...attributes} setColor={setHue} currentValue={hue} label='Hue' max={360} setOpposing={setRgb} name='hue' />
<ValueRange {...attributes} setColor={setSaturation} currentValue={saturation} label='Saturation' setOpposing={setRgb} name='saturation' />
<ValueRange {...attributes} setColor={setLightness} currentValue={lightness} label='Lightness' setOpposing={setRgb} name='lightness' />
</div>
<div class="box">
<h2>RGB</h2>
<ValueRange {...attributes} setColor={setRed} currentValue={red} label='Red' setOpposing={setHsl} max={255} name='red' />
<ValueRange {...attributes} setColor={setGreen} currentValue={green} label='Green' setOpposing={setHsl} max={255} name='green' />
<ValueRange {...attributes} setColor={setBlue} currentValue={blue} label='Blue' setOpposing={setHsl} max={255} name='blue' />
</div>
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('app'));
View Compiled
This Pen doesn't use any external CSS resources.