增加颜色对比功能。
This commit is contained in:
parent
0eb00122c8
commit
1edc74daaf
|
@ -3,6 +3,7 @@ import { ColorFunctionProvider } from './ColorFunctionContext';
|
||||||
import { Notifications } from './components/Notifications';
|
import { Notifications } from './components/Notifications';
|
||||||
import { ColorCards } from './pages/Cards';
|
import { ColorCards } from './pages/Cards';
|
||||||
import { CardsDetail } from './pages/CardsDetail';
|
import { CardsDetail } from './pages/CardsDetail';
|
||||||
|
import { ColorCompare } from './pages/Compare';
|
||||||
import { Harmonies } from './pages/Harmonies';
|
import { Harmonies } from './pages/Harmonies';
|
||||||
import { Home } from './pages/Home';
|
import { Home } from './pages/Home';
|
||||||
import { LightenDarken } from './pages/LightenDarken';
|
import { LightenDarken } from './pages/LightenDarken';
|
||||||
|
@ -39,6 +40,7 @@ const routes = createBrowserRouter([
|
||||||
{ path: 'lighten-darken', element: <LightenDarken /> },
|
{ path: 'lighten-darken', element: <LightenDarken /> },
|
||||||
{ path: 'mixer', element: <Mixer /> },
|
{ path: 'mixer', element: <Mixer /> },
|
||||||
{ path: 'wacg', element: <WACGCheck /> },
|
{ path: 'wacg', element: <WACGCheck /> },
|
||||||
|
{ path: 'compare', element: <ColorCompare /> },
|
||||||
{
|
{
|
||||||
path: 'cards',
|
path: 'cards',
|
||||||
element: <ColorCards />,
|
element: <ColorCards />,
|
||||||
|
|
25
src/page-components/color-compare/CompareLayout.module.css
Normal file
25
src/page-components/color-compare/CompareLayout.module.css
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
@layer pages {
|
||||||
|
.elements {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
.element {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
.element_name {
|
||||||
|
font-size: var(--font-size-xxl);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.element_values {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
line-height: var(--font-size-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/page-components/color-compare/HCTCompare.tsx
Normal file
74
src/page-components/color-compare/HCTCompare.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { HctDiffference } from '../../color_functions/color_module';
|
||||||
|
import { useColorFunction } from '../../ColorFunctionContext';
|
||||||
|
import styles from './CompareLayout.module.css';
|
||||||
|
import { CompareMethodProps } from './share-props';
|
||||||
|
|
||||||
|
const defaultCompareResult: HctDiffference = {
|
||||||
|
hue: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
chroma: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
lightness: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function HCTCompare({ basic = '000000', compare = '000000' }: CompareMethodProps) {
|
||||||
|
const { colorFn } = useColorFunction();
|
||||||
|
const differ = useMemo(() => {
|
||||||
|
if (!colorFn) {
|
||||||
|
return defaultCompareResult;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const diff = colorFn.differ_in_hct(basic, compare);
|
||||||
|
return diff;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Compare in HCT]', e);
|
||||||
|
}
|
||||||
|
return defaultCompareResult;
|
||||||
|
}, [basic, compare]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h6>Compare in HCT Space</h6>
|
||||||
|
<div className={styles.elements}>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>H</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.hue.delta.toFixed(2)}</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.hue.percent) ? '0.00' : (differ.hue.percent * 100).toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>C</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.chroma.delta.toFixed(2)}</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.chroma.percent) ? '0.00' : (differ.chroma.percent * 100).toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>T</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.lightness.delta.toFixed(2)}</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.lightness.percent)
|
||||||
|
? '0.00'
|
||||||
|
: (differ.lightness.percent * 100).toFixed(2)}
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
77
src/page-components/color-compare/HSLCompare.tsx
Normal file
77
src/page-components/color-compare/HSLCompare.tsx
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { HSLDifference } from '../../color_functions/color_module';
|
||||||
|
import { useColorFunction } from '../../ColorFunctionContext';
|
||||||
|
import styles from './CompareLayout.module.css';
|
||||||
|
import { CompareMethodProps } from './share-props';
|
||||||
|
|
||||||
|
const defaultCompareResult: HSLDifference = {
|
||||||
|
hue: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
saturation: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
lightness: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function HSLCompare({ basic = '000000', compare = '000000' }: CompareMethodProps) {
|
||||||
|
const { colorFn } = useColorFunction();
|
||||||
|
const differ = useMemo(() => {
|
||||||
|
if (!colorFn) {
|
||||||
|
return defaultCompareResult;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const diff = colorFn.differ_in_hsl(basic, compare);
|
||||||
|
return diff;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Compare in HSL]', e);
|
||||||
|
}
|
||||||
|
return defaultCompareResult;
|
||||||
|
}, [basic, compare]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h6>Compare in HSL Space</h6>
|
||||||
|
<div className={styles.elements}>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>H</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.hue.delta.toFixed(2)}</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.hue.percent) ? '0.00' : (differ.hue.percent * 100).toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>S</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{(differ.saturation.delta * 100).toFixed(2)}%</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.saturation.percent)
|
||||||
|
? '0.00'
|
||||||
|
: (differ.saturation.percent * 100).toFixed(2)}
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>L</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{(differ.lightness.delta * 100).toFixed(2)}%</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.lightness.percent)
|
||||||
|
? '0.00'
|
||||||
|
: (differ.lightness.percent * 100).toFixed(2)}
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
74
src/page-components/color-compare/OKLCHCompare.tsx
Normal file
74
src/page-components/color-compare/OKLCHCompare.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { OklchDifference } from '../../color_functions/color_module';
|
||||||
|
import { useColorFunction } from '../../ColorFunctionContext';
|
||||||
|
import styles from './CompareLayout.module.css';
|
||||||
|
import { CompareMethodProps } from './share-props';
|
||||||
|
|
||||||
|
const defaultCompareResult: OklchDifference = {
|
||||||
|
hue: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
chroma: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
lightness: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function OklchCompare({ basic = '000000', compare = '000000' }: CompareMethodProps) {
|
||||||
|
const { colorFn } = useColorFunction();
|
||||||
|
const differ = useMemo(() => {
|
||||||
|
if (!colorFn) {
|
||||||
|
return defaultCompareResult;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const diff = colorFn.differ_in_oklch(basic, compare);
|
||||||
|
return diff;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Compare in Oklch]', e);
|
||||||
|
}
|
||||||
|
return defaultCompareResult;
|
||||||
|
}, [basic, compare]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h6>Compare in Oklch Space</h6>
|
||||||
|
<div className={styles.elements}>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>L</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{(differ.lightness.delta * 100).toFixed(2)}%</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.lightness.percent)
|
||||||
|
? '0.0000'
|
||||||
|
: (differ.lightness.percent * 100).toFixed(2)}
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>C</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.chroma.delta.toFixed(4)}</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.chroma.percent) ? '0.0000' : (differ.chroma.percent * 100).toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>H</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.hue.delta.toFixed(2)}</span>
|
||||||
|
<span>
|
||||||
|
{isNaN(differ.hue.percent) ? '0.00' : (differ.hue.percent * 100).toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
78
src/page-components/color-compare/RGBCompare.tsx
Normal file
78
src/page-components/color-compare/RGBCompare.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { RGBDifference } from '../../color_functions/color_module';
|
||||||
|
import { useColorFunction } from '../../ColorFunctionContext';
|
||||||
|
import styles from './CompareLayout.module.css';
|
||||||
|
import { CompareMethodProps } from './share-props';
|
||||||
|
|
||||||
|
const defaultCompareResult: RGBDifference = {
|
||||||
|
r: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
g: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
delta: 0,
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function RGBCompare({ basic = '000000', compare = '000000' }: CompareMethodProps) {
|
||||||
|
const { colorFn } = useColorFunction();
|
||||||
|
const differ = useMemo(() => {
|
||||||
|
if (!colorFn) {
|
||||||
|
return defaultCompareResult;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const diff = colorFn?.differ_in_rgb(basic, compare);
|
||||||
|
return {
|
||||||
|
r: {
|
||||||
|
delta: Math.round(diff.r.delta * 255),
|
||||||
|
percent: diff.r.percent,
|
||||||
|
},
|
||||||
|
g: {
|
||||||
|
delta: Math.round(diff.g.delta * 255),
|
||||||
|
percent: diff.g.percent,
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
delta: Math.round(diff.b.delta * 255),
|
||||||
|
percent: diff.b.percent,
|
||||||
|
},
|
||||||
|
} as RGBDifference;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Compare in RGB]', e);
|
||||||
|
}
|
||||||
|
return defaultCompareResult;
|
||||||
|
}, [basic, compare]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h6>Compare in RGB Space</h6>
|
||||||
|
<div className={styles.elements}>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>R</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.r.delta}</span>
|
||||||
|
<span>{isNaN(differ.r.percent) ? '0.00' : (differ.r.percent * 100).toFixed(2)}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>G</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.g.delta}</span>
|
||||||
|
<span>{isNaN(differ.g.percent) ? '0.00' : (differ.g.percent * 100).toFixed(2)}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.element}>
|
||||||
|
<div className={styles.element_name}>B</div>
|
||||||
|
<div className={styles.element_values}>
|
||||||
|
<span>{differ.b.delta}</span>
|
||||||
|
<span>{isNaN(differ.b.percent) ? '0.00' : (differ.b.percent * 100).toFixed(2)}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
4
src/page-components/color-compare/share-props.ts
Normal file
4
src/page-components/color-compare/share-props.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export type CompareMethodProps = {
|
||||||
|
basic?: string;
|
||||||
|
compare?: string;
|
||||||
|
};
|
18
src/pages/Compare.module.css
Normal file
18
src/pages/Compare.module.css
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
@layer pages {
|
||||||
|
.compare_workspace {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.compare_content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
.compare_column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/pages/Compare.tsx
Normal file
44
src/pages/Compare.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import cx from 'clsx';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { ColorPicker } from '../components/ColorPicker';
|
||||||
|
import { ScrollArea } from '../components/ScrollArea';
|
||||||
|
import { HCTCompare } from '../page-components/color-compare/HCTCompare';
|
||||||
|
import { HSLCompare } from '../page-components/color-compare/HSLCompare';
|
||||||
|
import { OklchCompare } from '../page-components/color-compare/OKLCHCompare';
|
||||||
|
import { RGBCompare } from '../page-components/color-compare/RGBCompare';
|
||||||
|
import styles from './Compare.module.css';
|
||||||
|
|
||||||
|
export function ColorCompare() {
|
||||||
|
const [basicColor, setBasicColor] = useState('000000');
|
||||||
|
const [compareColor, setCompareColor] = useState('000000');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('workspace', styles.compare_workspace)}>
|
||||||
|
<header>
|
||||||
|
<h3>Color Compare</h3>
|
||||||
|
<p>
|
||||||
|
Compare the properties of two colors and find the associated patterns of color change.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<ScrollArea enableY>
|
||||||
|
<div className={styles.compare_content}>
|
||||||
|
<div className={styles.compare_column}>
|
||||||
|
<h5>Basic Color</h5>
|
||||||
|
<ColorPicker color={basicColor} onSelect={setBasicColor} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.compare_column}>
|
||||||
|
<h5>Compare Color</h5>
|
||||||
|
<ColorPicker color={compareColor} onSelect={setCompareColor} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.compare_column}>
|
||||||
|
<h5>Compare Result</h5>
|
||||||
|
<RGBCompare basic={basicColor} compare={compareColor} />
|
||||||
|
<HSLCompare basic={basicColor} compare={compareColor} />
|
||||||
|
<HCTCompare basic={basicColor} compare={compareColor} />
|
||||||
|
<OklchCompare basic={basicColor} compare={compareColor} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user