完成色轮选色的基础功能。

This commit is contained in:
徐涛
2025-01-03 17:22:10 +08:00
parent b206a58be1
commit 10f885526e
7 changed files with 274 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
@layer pages {
@keyframes current_blink {
0% {
opacity: 0.2;
}
50% {
opacity: 0.5;
}
100% {
opacity: 0.2;
}
}
.color_column {
clip-path: polygon(38.5% 0px, 50% 100%, 61.25% 0px);
position: absolute;
top: 0;
left: 0;
width: 100%;
transform-origin: center bottom;
}
.color_block {
position: relative;
height: 1.5em;
width: 100%;
.bg {
position: absolute;
inset: 0;
}
.dim_overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 70%);
opacity: 0;
}
.show_overlay {
opacity: 0.8;
transition: opacity 500ms ease-in-out;
}
.active {
position: absolute;
inset: 0;
background-color: var(--color-white);
animation: current_blink 2s linear infinite;
}
}
}

View File

@@ -0,0 +1,56 @@
import cx from 'clsx';
import { isEqual } from 'lodash-es';
import { useMemo } from 'react';
import { useColorFunction } from '../../ColorFunctionContext';
import { useCopyColor } from '../../hooks/useCopyColor';
import styles from './ColorColumn.module.css';
type ColorBlockProps = {
dimmed: boolean;
color: string;
isRoot?: boolean;
};
function ColorBlock({ dimmed, color, isRoot = false }: ColorBlockProps) {
const copyColor = useCopyColor();
return (
<div className={styles.color_block} onClick={() => copyColor(color)}>
<div className={styles.bg} style={{ backgroundColor: `#${color}` }} />
<div className={cx(styles.dim_overlay, dimmed && styles.show_overlay)} />
<div className={cx(isRoot && styles.active)} />
</div>
);
}
type ColorWheelProps = {
actived: boolean;
rotate: number;
rootColor: string;
};
export function ColorColumn({ actived, rotate, rootColor }: ColorWheelProps) {
const { colorFn } = useColorFunction();
const colorSeries = useMemo(() => {
try {
const colors = colorFn?.series(rootColor, 4, 0.16);
return (colors ?? Array.from({ length: 9 }, () => rootColor)).reverse();
} catch (e) {
console.error('[Generate Color Series]', e);
}
return Array.from({ length: 9 }, () => rootColor);
}, [rootColor]);
return (
<div className={styles.color_column} style={{ transform: `rotate(${rotate}deg)` }}>
{colorSeries.map((c, index) => (
<ColorBlock
key={`${c}-${index}`}
dimmed={!actived}
color={c}
isRoot={isEqual(c, rootColor) && isEqual(rotate, 0)}
/>
))}
</div>
);
}

View File

@@ -0,0 +1,22 @@
@layer pages {
.wheel_view {
width: 100%;
height: 100%;
padding: 0 var(--spacing-m);
display: flex;
flex-direction: column;
h5 {
padding-block: var(--spacing-m);
font-size: var(--font-size-m);
}
.wheel_place {
flex: 1;
padding: var(--spacing-m) 0;
}
.wheel_container {
position: relative;
aspect-ratio: 1 / 1;
height: 100%;
}
}
}

View File

@@ -0,0 +1,67 @@
import cx from 'clsx';
import { includes } from 'lodash-es';
import { useMemo } from 'react';
import { useColorFunction } from '../../ColorFunctionContext';
import { ColorColumn } from './ColorColumn';
import styles from './ColorWheel.module.css';
type ColorWheelProps = {
originColor?: string;
highlightMode?:
| 'complementary'
| 'analogous'
| 'analogous_with_complementary'
| 'triadic'
| 'split_complementary'
| 'tetradic'
| 'flip_tetradic'
| 'square';
};
const wheelRotates = [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330];
const highlightSeries = {
complementary: [0, 180],
analogous: [0, 30, 330],
analogous_with_complementary: [0, 30, 180, 330],
triadic: [0, 120, 240],
split_complementary: [0, 150, 210],
tetradic: [0, 60, 180, 240],
flip_tetradic: [0, 120, 180, 300],
square: [0, 90, 180, 270],
};
export function ColorWheel({
originColor = '000000',
highlightMode = 'complementary',
}: ColorWheelProps) {
const { colorFn } = useColorFunction();
const wheelColors = useMemo(() => {
return wheelRotates.map((rotate) => {
try {
const color = colorFn?.shift_hue(originColor, rotate);
return { color: color ?? '000000', rotate };
} catch (e) {
console.error('[Generate color wheel]', e);
}
return { color: '000000', rotate };
});
}, [originColor]);
return (
<div className={styles.wheel_view}>
<h5>Color Wheel</h5>
<div className={cx('center', styles.wheel_place)}>
<div className={styles.wheel_container}>
{wheelColors.map(({ color, rotate }) => (
<ColorColumn
key={`${color}-${rotate}`}
rootColor={color}
rotate={rotate}
actived={includes(highlightSeries[highlightMode], rotate)}
/>
))}
</div>
</div>
</div>
);
}