From 996906488e64d2466472b0f569ef0ff22fa61648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Tue, 7 Jan 2025 09:22:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90Tints=20&=20Shades=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 + src/page-components/TintsShades/shades.tsx | 59 +++++++++ src/page-components/TintsShades/tints.tsx | 59 +++++++++ src/pages/TintsShades.module.css | 55 +++++++++ src/pages/TintsShades.tsx | 134 +++++++++++++++++++++ 5 files changed, 309 insertions(+) create mode 100644 src/page-components/TintsShades/shades.tsx create mode 100644 src/page-components/TintsShades/tints.tsx create mode 100644 src/pages/TintsShades.module.css create mode 100644 src/pages/TintsShades.tsx diff --git a/src/App.tsx b/src/App.tsx index 2eb9908..db7cfe8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 { TintsShades } from './pages/TintsShades'; import { Tones } from './pages/Tones'; import { Wheels } from './pages/Wheels'; @@ -29,6 +30,7 @@ const routes = createBrowserRouter([ { path: 'harmonies', element: }, { path: 'wheels', element: }, { path: 'tones', element: }, + { path: 'tints-shades', element: }, ], }, ]); diff --git a/src/page-components/TintsShades/shades.tsx b/src/page-components/TintsShades/shades.tsx new file mode 100644 index 0000000..bbc999a --- /dev/null +++ b/src/page-components/TintsShades/shades.tsx @@ -0,0 +1,59 @@ +import { last } from 'lodash-es'; +import { useMemo } from 'react'; +import { useColorFunction } from '../../ColorFunctionContext'; +import { FlexColorStand } from '../../components/FlexColorStand'; + +type ShadesListProps = { + color: string; + shades: number; + mix: 'progressive' | 'linear' | 'average'; + step?: number; + maximum?: number; + copyMode: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'; +}; + +export function Shades({ color, shades, mix, step, maximum, copyMode }: ShadesListProps) { + const { colorFn } = useColorFunction(); + const colors = useMemo(() => { + try { + if (!colorFn) { + return Array.from({ length: shades + 1 }, () => color); + } + const genColors = [color]; + switch (mix) { + case 'progressive': + for (let i = 1; i <= shades; i++) { + const shade = colorFn!.shade(last(genColors), step); + genColors.push(shade); + } + break; + case 'linear': + for (let i = 1; i <= shades; i++) { + const shade = colorFn!.shade(color, step * i); + genColors.push(shade); + } + break; + case 'average': { + const interval = maximum / shades / 100; + for (let i = 1; i <= shades; i++) { + const shade = colorFn!.shade(color, interval * i); + genColors.push(shade); + } + break; + } + } + return genColors.reverse(); + } catch (e) { + console.error('[Generate Shades]', e); + } + return Array.from({ length: shades + 1 }, () => color); + }, [color, shades, mix, step, maximum]); + + return ( + <> + {colors.map((c, index) => ( + + ))} + + ); +} diff --git a/src/page-components/TintsShades/tints.tsx b/src/page-components/TintsShades/tints.tsx new file mode 100644 index 0000000..748ec1c --- /dev/null +++ b/src/page-components/TintsShades/tints.tsx @@ -0,0 +1,59 @@ +import { last } from 'lodash-es'; +import { useMemo } from 'react'; +import { useColorFunction } from '../../ColorFunctionContext'; +import { FlexColorStand } from '../../components/FlexColorStand'; + +type TintsListProps = { + color: string; + tints: number; + mix: 'progressive' | 'linear' | 'average'; + step?: number; + maximum?: number; + copyMode: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'; +}; + +export function Tints({ color, tints, mix, step, maximum, copyMode }: TintsListProps) { + const { colorFn } = useColorFunction(); + const colors = useMemo(() => { + try { + if (!colorFn) { + return Array.from({ length: tints + 1 }, () => color); + } + const genColors = [color]; + switch (mix) { + case 'progressive': + for (let i = 1; i <= tints; i++) { + const tint = colorFn!.tint(last(genColors), step); + genColors.push(tint); + } + break; + case 'linear': + for (let i = 1; i <= tints; i++) { + const tint = colorFn!.tint(color, step * i); + genColors.push(tint); + } + break; + case 'average': { + const interval = maximum / tints / 100; + for (let i = 1; i <= tints; i++) { + const tint = colorFn!.tint(color, interval * i); + genColors.push(tint); + } + break; + } + } + return genColors; + } catch (e) { + console.error('[Generate Tints]', e); + } + return Array.from({ length: tints + 1 }, () => color); + }, [color, tints, mix, step, maximum]); + + return ( + <> + {colors.map((c, index) => ( + + ))} + + ); +} diff --git a/src/pages/TintsShades.module.css b/src/pages/TintsShades.module.css new file mode 100644 index 0000000..782a354 --- /dev/null +++ b/src/pages/TintsShades.module.css @@ -0,0 +1,55 @@ +@layer pages { + .tints_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); + } + } + .tints_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; + } + } +} diff --git a/src/pages/TintsShades.tsx b/src/pages/TintsShades.tsx new file mode 100644 index 0000000..8f4376d --- /dev/null +++ b/src/pages/TintsShades.tsx @@ -0,0 +1,134 @@ +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 { Shades } from '../page-components/TintsShades/shades'; +import { Tints } from '../page-components/TintsShades/tints'; +import { currentPickedColor } from '../stores/colors'; +import styles from './TintsShades.module.css'; + +export function TintsShades() { + const [selectedColor, setSelectedColor] = useAtom(currentPickedColor); + const [steps, setSteps] = useState(10); + const [tints, setTints] = useState(3); + const [shades, setShades] = useState(3); + const [maximum, setMaximum] = useState(90); + const [mixMode, setMixMode] = useState<'progressive' | 'average'>('progressive'); + const [mode, setMode] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex'); + + return ( +
+
+

Tints & Shades

+

By proportionally mixing black and white, generating a series of varying colors.

+
+ +
+ +
+
Tints
+
+ +
+
Shades
+
+ +
+
+ + +
+
+
+
+
+ ); +}