完成Lighten & Darken功能。
This commit is contained in:
parent
6c9f337a22
commit
f3eceda560
|
@ -3,6 +3,7 @@ import { ColorFunctionProvider } from './ColorFunctionContext';
|
|||
import { Notifications } from './components/Notifications';
|
||||
import { Harmonies } from './pages/Harmonies';
|
||||
import { Home } from './pages/Home';
|
||||
import { LightenDarken } from './pages/LightenDarken';
|
||||
import { MainLayout } from './pages/MainLayout';
|
||||
import { NewScheme } from './pages/NewScheme';
|
||||
import { SchemeDetail } from './pages/SchemeDetail';
|
||||
|
@ -31,6 +32,7 @@ const routes = createBrowserRouter([
|
|||
{ path: 'wheels', element: <Wheels /> },
|
||||
{ path: 'tones', element: <Tones /> },
|
||||
{ path: 'tints-shades', element: <TintsShades /> },
|
||||
{ path: 'lighten-darken', element: <LightenDarken /> },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
|
59
src/page-components/lighten-darken/darkens.tsx
Normal file
59
src/page-components/lighten-darken/darkens.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { last } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { useColorFunction } from '../../ColorFunctionContext';
|
||||
import { FlexColorStand } from '../../components/FlexColorStand';
|
||||
|
||||
type DarkensProps = {
|
||||
color: string;
|
||||
darkens: number;
|
||||
mix: 'progressive' | 'linear' | 'average';
|
||||
step?: number;
|
||||
maximum?: number;
|
||||
copyMode: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
|
||||
};
|
||||
|
||||
export function Darkens({ color, darkens, mix, step, maximum, copyMode }: DarkensProps) {
|
||||
const { colorFn } = useColorFunction();
|
||||
const colors = useMemo(() => {
|
||||
try {
|
||||
if (!colorFn) {
|
||||
return Array.from({ length: darkens + 1 }, () => color);
|
||||
}
|
||||
const darkenColors = [color];
|
||||
switch (mix) {
|
||||
case 'progressive':
|
||||
for (let i = 1; i <= darkens; i++) {
|
||||
const darkenColor = colorFn.darken(last(darkenColors), step);
|
||||
darkenColors.push(darkenColor);
|
||||
}
|
||||
break;
|
||||
case 'linear':
|
||||
for (let i = 1; i <= darkens; i++) {
|
||||
const darkenColor = colorFn.darken(color, step * i);
|
||||
darkenColors.push(darkenColor);
|
||||
}
|
||||
break;
|
||||
case 'average': {
|
||||
const interval = maximum / darkens / 100;
|
||||
for (let i = 1; i <= darkens; i++) {
|
||||
const darkenColor = colorFn.darken(color, interval * i);
|
||||
darkenColors.push(darkenColor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return darkenColors.reverse();
|
||||
} catch (e) {
|
||||
console.error('[Generate Lighten]', e);
|
||||
}
|
||||
return Array.from({ length: darkens + 1 }, () => color);
|
||||
}, [color, darkens, mix, step, maximum]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{colors.map((c, index) => (
|
||||
<FlexColorStand key={`${c}-${index}`} color={c} valueMode={copyMode} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
59
src/page-components/lighten-darken/lightens.tsx
Normal file
59
src/page-components/lighten-darken/lightens.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { last } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { useColorFunction } from '../../ColorFunctionContext';
|
||||
import { FlexColorStand } from '../../components/FlexColorStand';
|
||||
|
||||
type LightensProps = {
|
||||
color: string;
|
||||
lightens: number;
|
||||
mix: 'progressive' | 'linear' | 'average';
|
||||
step?: number;
|
||||
maximum?: number;
|
||||
copyMode: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
|
||||
};
|
||||
|
||||
export function Lightens({ color, lightens, mix, step, maximum, copyMode }: LightensProps) {
|
||||
const { colorFn } = useColorFunction();
|
||||
const colors = useMemo(() => {
|
||||
try {
|
||||
if (!colorFn) {
|
||||
return Array.from({ length: lightens + 1 }, () => color);
|
||||
}
|
||||
const lightenColors = [color];
|
||||
switch (mix) {
|
||||
case 'progressive':
|
||||
for (let i = 1; i <= lightens; i++) {
|
||||
const lightenColor = colorFn.lighten(last(lightenColors), step);
|
||||
lightenColors.push(lightenColor);
|
||||
}
|
||||
break;
|
||||
case 'linear':
|
||||
for (let i = 1; i <= lightens; i++) {
|
||||
const lightenColor = colorFn.lighten(color, step * i);
|
||||
lightenColors.push(lightenColor);
|
||||
}
|
||||
break;
|
||||
case 'average': {
|
||||
const interval = maximum / lightens / 100;
|
||||
for (let i = 1; i <= lightens; i++) {
|
||||
const lightenColor = colorFn.lighten(color, interval * i);
|
||||
lightenColors.push(lightenColor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lightenColors;
|
||||
} catch (e) {
|
||||
console.error('[Generate Lighten]', e);
|
||||
}
|
||||
return Array.from({ length: lightens + 1 }, () => color);
|
||||
}, [color, lightens, mix, step, maximum]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{colors.map((c, index) => (
|
||||
<FlexColorStand key={`${c}-${index}`} color={c} valueMode={copyMode} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
55
src/pages/LightenDarken.module.css
Normal file
55
src/pages/LightenDarken.module.css
Normal file
|
@ -0,0 +1,55 @@
|
|||
@layer pages {
|
||||
.lighten_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);
|
||||
}
|
||||
}
|
||||
.lights_content {
|
||||
flex: 1;
|
||||
padding: 0 var(--spacing-m);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-s);
|
||||
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: 12em;
|
||||
}
|
||||
}
|
||||
}
|
135
src/pages/LightenDarken.tsx
Normal file
135
src/pages/LightenDarken.tsx
Normal file
|
@ -0,0 +1,135 @@
|
|||
import cx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useState } from 'react';
|
||||
import { ColorPicker } from '../components/ColorPicker';
|
||||
import { HSegmentedControl } from '../components/HSegmentedControl';
|
||||
import { Labeled } from '../components/Labeled';
|
||||
import { LabeledPicker } from '../components/LabeledPicker';
|
||||
import { ScrollArea } from '../components/ScrollArea';
|
||||
import { VSegmentedControl } from '../components/VSegmentedControl';
|
||||
import { Darkens } from '../page-components/lighten-darken/darkens';
|
||||
import { Lightens } from '../page-components/lighten-darken/lightens';
|
||||
import { currentPickedColor } from '../stores/colors';
|
||||
import styles from './LightenDarken.module.css';
|
||||
|
||||
export function LightenDarken() {
|
||||
const [selectedColor, setSelectedColor] = useAtom(currentPickedColor);
|
||||
const [lighten, setLighten] = useState(3);
|
||||
const [darken, setDarken] = useState(3);
|
||||
const [steps, setSteps] = useState(10);
|
||||
const [maximum, setMaximum] = useState(90);
|
||||
const [mixMode, setMixMode] = useState<'progressive' | 'average'>('progressive');
|
||||
const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
|
||||
|
||||
return (
|
||||
<div className={cx('workspace', styles.lighten_workspace)}>
|
||||
{' '}
|
||||
<header>
|
||||
<h3>Lighten & Darken</h3>
|
||||
<p>By varying the brightness of a specified color, produced a series of colors.</p>
|
||||
</header>
|
||||
<ScrollArea enableY>
|
||||
<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="Lighten"
|
||||
min={1}
|
||||
max={10}
|
||||
step={1}
|
||||
value={lighten}
|
||||
onChange={setLighten}
|
||||
/>
|
||||
<LabeledPicker
|
||||
title="Darken"
|
||||
min={1}
|
||||
max={10}
|
||||
step={1}
|
||||
value={darken}
|
||||
onChange={setDarken}
|
||||
/>
|
||||
<Labeled label="Generate Mode">
|
||||
<VSegmentedControl
|
||||
options={[
|
||||
{ label: 'Progressive', value: 'progressive' },
|
||||
{ label: 'Linear', value: 'linear' },
|
||||
{ label: 'Average', value: 'average' },
|
||||
]}
|
||||
value={mixMode}
|
||||
onChange={(value) => {
|
||||
setMixMode(value as 'progressive' | 'average');
|
||||
console.debug('[Mix Mode Switch]', value);
|
||||
}}
|
||||
/>
|
||||
</Labeled>
|
||||
{(isEqual(mixMode, 'progressive') || isEqual(mixMode, 'linear')) && (
|
||||
<LabeledPicker
|
||||
title="Step"
|
||||
min={10}
|
||||
max={25}
|
||||
step={1}
|
||||
value={steps}
|
||||
onChange={setSteps}
|
||||
/>
|
||||
)}
|
||||
{isEqual(mixMode, 'average') && (
|
||||
<LabeledPicker
|
||||
title="Maximum"
|
||||
min={10}
|
||||
max={100}
|
||||
step={1}
|
||||
value={maximum}
|
||||
onChange={setMaximum}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
<div className={styles.lights_content}>
|
||||
<h5>Lighten Series</h5>
|
||||
<div className={styles.colors_booth}>
|
||||
<Lightens
|
||||
color={selectedColor}
|
||||
step={steps / 100}
|
||||
lightens={lighten}
|
||||
mix={mixMode}
|
||||
maximum={maximum}
|
||||
copyMode={mode}
|
||||
/>
|
||||
</div>
|
||||
<h5>Darken Series</h5>
|
||||
<div className={styles.colors_booth}>
|
||||
<Darkens
|
||||
color={selectedColor}
|
||||
step={steps / 100}
|
||||
darkens={darken}
|
||||
mix={mixMode}
|
||||
maximum={maximum}
|
||||
copyMode={mode}
|
||||
/>
|
||||
</div>
|
||||
<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>
|
||||
</section>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user