增加色调调色功能。
This commit is contained in:
parent
4effdb0847
commit
e3b3151bba
|
@ -8,6 +8,7 @@ import { NewScheme } from './pages/NewScheme';
|
|||
import { SchemeDetail } from './pages/SchemeDetail';
|
||||
import { SchemeNotFound } from './pages/SchemeNotFound';
|
||||
import { Schemes } from './pages/Schemes';
|
||||
import { Tones } from './pages/Tones';
|
||||
import { Wheels } from './pages/Wheels';
|
||||
|
||||
const routes = createBrowserRouter([
|
||||
|
@ -27,6 +28,7 @@ const routes = createBrowserRouter([
|
|||
},
|
||||
{ path: 'harmonies', element: <Harmonies /> },
|
||||
{ path: 'wheels', element: <Wheels /> },
|
||||
{ path: 'tones', element: <Tones /> },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
|
62
src/page-components/tones/ToneSeries.tsx
Normal file
62
src/page-components/tones/ToneSeries.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useColorFunction } from '../../ColorFunctionContext';
|
||||
import { FlexColorStand } from '../../components/FlexColorStand';
|
||||
|
||||
type ToneSeresProps = {
|
||||
color?: string;
|
||||
expand?: number;
|
||||
step?: number;
|
||||
valueMode?: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
|
||||
};
|
||||
|
||||
export function LightenSeries({
|
||||
color = '000000',
|
||||
expand = 3,
|
||||
step = 10,
|
||||
valueMode = 'hex',
|
||||
}: ToneSeresProps) {
|
||||
const { colorFn } = useColorFunction();
|
||||
const colors = useMemo(() => {
|
||||
try {
|
||||
const lightenColors = colorFn!.tonal_lighten_series(color, expand, step / 100);
|
||||
return lightenColors;
|
||||
} catch (e) {
|
||||
console.error('[Expand lighten colors]', e);
|
||||
}
|
||||
return Array.from({ length: expand }, () => color);
|
||||
}, [color, expand, step]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{colors.map((c, index) => (
|
||||
<FlexColorStand key={`${c}-${index}`} color={c} valueMode={valueMode} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function DarkenSeries({
|
||||
color = '000000',
|
||||
expand = 3,
|
||||
step = 10,
|
||||
valueMode = 'hex',
|
||||
}: ToneSeresProps) {
|
||||
const { colorFn } = useColorFunction();
|
||||
const colors = useMemo(() => {
|
||||
try {
|
||||
const darkenColors = colorFn!.tonal_darken_series(color, expand, step / 100);
|
||||
return darkenColors;
|
||||
} catch (e) {
|
||||
console.error('[Expand darken colors]', e);
|
||||
}
|
||||
return Array.from({ length: expand }, () => color);
|
||||
}, [color, expand, step]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{colors.map((c, index) => (
|
||||
<FlexColorStand key={`${c}-${index}`} color={c} valueMode={valueMode} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
55
src/pages/Tones.module.css
Normal file
55
src/pages/Tones.module.css
Normal file
|
@ -0,0 +1,55 @@
|
|||
@layer pages {
|
||||
.tone_workspace {
|
||||
flex-direction: column;
|
||||
}
|
||||
.explore_section {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
.function_side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-m);
|
||||
font-size: var(--font-size-s);
|
||||
.mode_navigation {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
h5 {
|
||||
padding-block: var(--spacing-m);
|
||||
font-size: var(--font-size-m);
|
||||
}
|
||||
}
|
||||
.tones_content {
|
||||
flex: 1;
|
||||
padding: 0 var(--spacing-m);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
h5 {
|
||||
padding-block: var(--spacing-m);
|
||||
font-size: var(--font-size-m);
|
||||
}
|
||||
.color_value_mode {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--spacing-s);
|
||||
font-size: var(--font-size-s);
|
||||
}
|
||||
.colors_booth {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-xs);
|
||||
min-height: 15em;
|
||||
}
|
||||
}
|
||||
}
|
98
src/pages/Tones.tsx
Normal file
98
src/pages/Tones.tsx
Normal file
|
@ -0,0 +1,98 @@
|
|||
import cx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { toInteger } from 'lodash-es';
|
||||
import { useState } from 'react';
|
||||
import { ColorPicker } from '../components/ColorPicker';
|
||||
import { FlexColorStand } from '../components/FlexColorStand';
|
||||
import { HSegmentedControl } from '../components/HSegmentedControl';
|
||||
import { LabeledPicker } from '../components/LabeledPicker';
|
||||
import { DarkenSeries, LightenSeries } from '../page-components/tones/ToneSeries';
|
||||
import { currentPickedColor } from '../stores/colors';
|
||||
import styles from './Tones.module.css';
|
||||
|
||||
export function Tones() {
|
||||
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
|
||||
const [steps, setSteps] = useState(10);
|
||||
const [tones, setTones] = useState(3);
|
||||
const [seedBias, setSeedBias] = useState(0);
|
||||
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
|
||||
|
||||
return (
|
||||
<div className={cx('workspace', styles.tone_workspace)}>
|
||||
<header>
|
||||
<h3>Color Tones</h3>
|
||||
<p>By regularly changing the color hue to generate a series of light and dark colors.</p>
|
||||
</header>
|
||||
<section className={styles.explore_section}>
|
||||
<aside className={styles.function_side}>
|
||||
<div>
|
||||
<h5>Basic color</h5>
|
||||
<ColorPicker color={selectedColor} onSelect={(color) => setSelectedColor(color)} />
|
||||
</div>
|
||||
<div>
|
||||
<h5>Series Setting</h5>
|
||||
<LabeledPicker
|
||||
title="Expand Tones"
|
||||
min={1}
|
||||
max={5}
|
||||
step={1}
|
||||
value={tones}
|
||||
onChange={(value) => {
|
||||
setTones(toInteger(value));
|
||||
setSeedBias(0);
|
||||
}}
|
||||
/>
|
||||
<LabeledPicker
|
||||
title="Root bias"
|
||||
min={-(tones - 1)}
|
||||
max={tones - 1}
|
||||
value={seedBias}
|
||||
onChange={(value) => setSeedBias(toInteger(value))}
|
||||
/>
|
||||
<LabeledPicker
|
||||
title="Tone Step"
|
||||
min={10}
|
||||
max={25}
|
||||
step={1}
|
||||
unit="%"
|
||||
value={steps}
|
||||
onChange={setSteps}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
<div className={styles.tones_content}>
|
||||
<h5>Color Tones</h5>
|
||||
<div className={styles.color_value_mode}>
|
||||
<label>Copy color value in</label>
|
||||
<HSegmentedControl
|
||||
options={[
|
||||
{ label: 'HEX', value: 'hex' },
|
||||
{ label: 'RGB', value: 'rgb' },
|
||||
{ label: 'HSL', value: 'hsl' },
|
||||
{ label: 'LAB', value: 'lab' },
|
||||
{ label: 'OKLCH', value: 'oklch' },
|
||||
]}
|
||||
valu={mode}
|
||||
onChange={setMode}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.colors_booth}>
|
||||
<DarkenSeries
|
||||
color={selectedColor}
|
||||
expand={tones + seedBias}
|
||||
step={steps}
|
||||
valueMode={mode}
|
||||
/>
|
||||
<FlexColorStand color={selectedColor} valueMode={mode} />
|
||||
<LightenSeries
|
||||
color={selectedColor}
|
||||
expand={tones - seedBias}
|
||||
step={steps}
|
||||
valueMode={mode}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user