Compare commits

...

6 Commits

Author SHA1 Message Date
徐涛
1edc74daaf 增加颜色对比功能。 2025-01-13 17:08:33 +08:00
徐涛
0eb00122c8 调整颜色拾取器的最小宽度。 2025-01-13 17:03:07 +08:00
徐涛
deed113eae 调整Color Mixer的整体布局。 2025-01-13 15:46:55 +08:00
徐涛
8ceeac545d 生成可用的WASM包。 2025-01-13 15:26:05 +08:00
徐涛
a012f28c47 增加指定对比两个颜色的功能。 2025-01-13 15:11:03 +08:00
徐涛
0f343c606e 尝试重新设计配色方案。 2025-01-13 13:55:48 +08:00
25 changed files with 1294 additions and 2 deletions

View File

@ -0,0 +1,38 @@
use palette::cam16::Cam16Jch;
use serde::Serialize;
use wasm_bindgen::prelude::wasm_bindgen;
use super::{ColorDifference, Differ};
#[derive(Debug, Clone, Copy, Serialize)]
#[wasm_bindgen]
pub struct HctDiffference {
pub hue: Differ,
pub chroma: Differ,
pub lightness: Differ,
}
impl ColorDifference for Cam16Jch<f32> {
type Difference = HctDiffference;
fn difference(&self, other: &Self) -> Self::Difference {
let hue = self.hue.into_positive_degrees() - other.hue.into_positive_degrees();
let chroma = self.chroma - other.chroma;
let lightness = self.lightness - other.lightness;
HctDiffference {
hue: Differ {
delta: hue,
percent: hue / self.hue.into_positive_degrees(),
},
chroma: Differ {
delta: chroma,
percent: chroma / self.chroma,
},
lightness: Differ {
delta: lightness,
percent: lightness / self.lightness,
},
}
}
}

View File

@ -0,0 +1,38 @@
use palette::Hsl;
use serde::Serialize;
use wasm_bindgen::prelude::wasm_bindgen;
use super::{ColorDifference, Differ};
#[derive(Debug, Clone, Copy, Serialize)]
#[wasm_bindgen]
pub struct HSLDifference {
pub hue: Differ,
pub saturation: Differ,
pub lightness: Differ,
}
impl ColorDifference for Hsl {
type Difference = HSLDifference;
fn difference(&self, other: &Self) -> Self::Difference {
let hue = self.hue.into_positive_degrees() - other.hue.into_positive_degrees();
let saturation = self.saturation - other.saturation;
let lightness = self.lightness - other.lightness;
HSLDifference {
hue: Differ {
delta: hue,
percent: hue / self.hue.into_positive_degrees(),
},
saturation: Differ {
delta: saturation,
percent: saturation / self.saturation,
},
lightness: Differ {
delta: lightness,
percent: lightness / self.lightness,
},
}
}
}

View File

@ -0,0 +1,20 @@
use serde::Serialize;
use wasm_bindgen::prelude::wasm_bindgen;
pub mod cam16jch;
pub mod hsl;
pub mod oklch;
pub mod rgb;
#[derive(Debug, Clone, Copy, Serialize)]
#[wasm_bindgen]
pub struct Differ {
pub delta: f32,
pub percent: f32,
}
pub trait ColorDifference {
type Difference;
fn difference(&self, other: &Self) -> Self::Difference;
}

View File

@ -0,0 +1,38 @@
use palette::Oklch;
use serde::Serialize;
use wasm_bindgen::prelude::wasm_bindgen;
use super::{ColorDifference, Differ};
#[derive(Debug, Clone, Copy, Serialize)]
#[wasm_bindgen]
pub struct OklchDifference {
pub hue: Differ,
pub chroma: Differ,
pub lightness: Differ,
}
impl ColorDifference for Oklch {
type Difference = OklchDifference;
fn difference(&self, other: &Self) -> Self::Difference {
let hue = self.hue.into_positive_degrees() - other.hue.into_positive_degrees();
let chroma = self.chroma - other.chroma;
let lightness = self.l - other.l;
OklchDifference {
hue: Differ {
delta: hue,
percent: hue / self.hue.into_positive_degrees(),
},
chroma: Differ {
delta: chroma,
percent: chroma / self.chroma,
},
lightness: Differ {
delta: lightness,
percent: lightness / self.l,
},
}
}
}

View File

@ -0,0 +1,38 @@
use palette::Srgb;
use serde::Serialize;
use wasm_bindgen::prelude::wasm_bindgen;
use super::{ColorDifference, Differ};
#[derive(Debug, Clone, Copy, Serialize)]
#[wasm_bindgen]
pub struct RGBDifference {
pub r: Differ,
pub g: Differ,
pub b: Differ,
}
impl ColorDifference for Srgb {
type Difference = RGBDifference;
fn difference(&self, other: &Self) -> Self::Difference {
let r = self.red - other.red;
let g = self.green - other.green;
let b = self.blue - other.blue;
RGBDifference {
r: Differ {
delta: r,
percent: r / self.red,
},
g: Differ {
delta: g,
percent: g / self.green,
},
b: Differ {
delta: b,
percent: b / self.blue,
},
}
}
}

View File

@ -1,6 +1,7 @@
use std::{str::FromStr, sync::Arc};
use color_card::Category;
use color_differ::ColorDifference;
use palette::{
cam16::{Cam16Jch, Parameters},
color_difference::Wcag21RelativeContrast,
@ -12,6 +13,7 @@ use strum::IntoEnumIterator;
use wasm_bindgen::prelude::*;
mod color_card;
mod color_differ;
mod errors;
#[wasm_bindgen]
@ -422,3 +424,75 @@ pub fn search_color_cards(tag: String, category: Option<String>) -> Result<JsVal
serde_wasm_bindgen::to_value(&cards).map_err(|e| e.to_string())
}
#[wasm_bindgen]
pub fn differ_in_rgb(
color: &str,
other: &str,
) -> Result<color_differ::rgb::RGBDifference, errors::ColorError> {
let srgb = Srgb::from_str(color)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
.into_format::<f32>();
let other_srgb = Srgb::from_str(other)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
.into_format::<f32>();
Ok(srgb.difference(&other_srgb))
}
#[wasm_bindgen]
pub fn differ_in_hsl(
color: &str,
other: &str,
) -> Result<color_differ::hsl::HSLDifference, errors::ColorError> {
let hsl = Hsl::from_color(
Srgb::from_str(color)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
.into_format::<f32>(),
);
let other_hsl = Hsl::from_color(
Srgb::from_str(other)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
.into_format::<f32>(),
);
Ok(hsl.difference(&other_hsl))
}
#[wasm_bindgen]
pub fn differ_in_hct(
color: &str,
other: &str,
) -> Result<color_differ::cam16jch::HctDiffference, errors::ColorError> {
let hct = Cam16Jch::from_xyz(
Srgb::from_str(color)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
.into_format::<f32>()
.into_color(),
Parameters::default_static_wp(40.0),
);
let other_hct = Cam16Jch::from_xyz(
Srgb::from_str(other)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
.into_format::<f32>()
.into_color(),
Parameters::default_static_wp(40.0),
);
Ok(hct.difference(&other_hct))
}
#[wasm_bindgen]
pub fn differ_in_oklch(
color: &str,
other: &str,
) -> Result<color_differ::oklch::OklchDifference, errors::ColorError> {
let oklch = Oklch::from_color(
Srgb::from_str(color)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
.into_format::<f32>(),
);
let other_oklch = Oklch::from_color(
Srgb::from_str(other)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
.into_format::<f32>(),
);
Ok(oklch.difference(&other_oklch))
}

View File

@ -3,6 +3,7 @@ import { ColorFunctionProvider } from './ColorFunctionContext';
import { Notifications } from './components/Notifications';
import { ColorCards } from './pages/Cards';
import { CardsDetail } from './pages/CardsDetail';
import { ColorCompare } from './pages/Compare';
import { Harmonies } from './pages/Harmonies';
import { Home } from './pages/Home';
import { LightenDarken } from './pages/LightenDarken';
@ -39,6 +40,7 @@ const routes = createBrowserRouter([
{ path: 'lighten-darken', element: <LightenDarken /> },
{ path: 'mixer', element: <Mixer /> },
{ path: 'wacg', element: <WACGCheck /> },
{ path: 'compare', element: <ColorCompare /> },
{
path: 'cards',
element: <ColorCards />,

46
src/color-q-scheme.ts Normal file
View File

@ -0,0 +1,46 @@
import { SchemeColor } from './models';
export type ColorSetVariant = {
chroma: number;
lightness: number;
};
export type Settings = {
hover: ColorSetVariant;
active: ColorSetVariant;
focus: ColorSetVariant;
disabled: ColorSetVariant;
foregroundRange: [SchemeColor, SchemeColor];
foregroundGeneration: 'fixed' | 'wacg_atuo';
};
export type SchemeSet = {
primary: SchemeColor;
onPrimary: SchemeColor;
secondary: SchemeColor;
onSecondary: SchemeColor;
accent: SchemeColor;
onAccent: SchemeColor;
neutral: SchemeColor;
onNeutral: SchemeColor;
danger: SchemeColor;
onDanger: SchemeColor;
warning: SchemeColor;
onWarning: SchemeColor;
success: SchemeColor;
onSuccess: SchemeColor;
info: SchemeColor;
onInfo: SchemeColor;
border: SchemeColor;
lightenBorder: SchemeColor;
elevation: SchemeColor;
background: SchemeColor;
onBackground: SchemeColor;
inverseBackground: SchemeColor;
onInverseBackground: SchemeColor;
};
export type ColorQScheme = {
scheme: SchemeSet;
setting: Settings;
};

View File

@ -30,6 +30,44 @@ export function tonal_lighten_series(color: string, expand_amount: number, step:
export function tonal_darken_series(color: string, expand_amount: number, step: number): (string)[];
export function color_categories(): any;
export function search_color_cards(tag: string, category?: string): any;
export function differ_in_rgb(color: string, other: string): RGBDifference;
export function differ_in_hsl(color: string, other: string): HSLDifference;
export function differ_in_hct(color: string, other: string): HctDiffference;
export function differ_in_oklch(color: string, other: string): OklchDifference;
export class Differ {
private constructor();
free(): void;
delta: number;
percent: number;
}
export class HSLDifference {
private constructor();
free(): void;
hue: Differ;
saturation: Differ;
lightness: Differ;
}
export class HctDiffference {
private constructor();
free(): void;
hue: Differ;
chroma: Differ;
lightness: Differ;
}
export class OklchDifference {
private constructor();
free(): void;
hue: Differ;
chroma: Differ;
lightness: Differ;
}
export class RGBDifference {
private constructor();
free(): void;
r: Differ;
g: Differ;
b: Differ;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@ -65,6 +103,43 @@ export interface InitOutput {
readonly tonal_darken_series: (a: number, b: number, c: number, d: number) => [number, number, number, number];
readonly color_categories: () => [number, number, number];
readonly search_color_cards: (a: number, b: number, c: number, d: number) => [number, number, number];
readonly differ_in_rgb: (a: number, b: number, c: number, d: number) => [number, number, number];
readonly differ_in_hsl: (a: number, b: number, c: number, d: number) => [number, number, number];
readonly differ_in_hct: (a: number, b: number, c: number, d: number) => [number, number, number];
readonly differ_in_oklch: (a: number, b: number, c: number, d: number) => [number, number, number];
readonly __wbg_oklchdifference_free: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_hue: (a: number) => number;
readonly __wbg_set_oklchdifference_hue: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_chroma: (a: number) => number;
readonly __wbg_set_oklchdifference_chroma: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_lightness: (a: number) => number;
readonly __wbg_set_oklchdifference_lightness: (a: number, b: number) => void;
readonly __wbg_differ_free: (a: number, b: number) => void;
readonly __wbg_get_differ_delta: (a: number) => number;
readonly __wbg_set_differ_delta: (a: number, b: number) => void;
readonly __wbg_get_differ_percent: (a: number) => number;
readonly __wbg_set_differ_percent: (a: number, b: number) => void;
readonly __wbg_hctdiffference_free: (a: number, b: number) => void;
readonly __wbg_get_hctdiffference_hue: (a: number) => number;
readonly __wbg_set_hctdiffference_hue: (a: number, b: number) => void;
readonly __wbg_get_hctdiffference_chroma: (a: number) => number;
readonly __wbg_set_hctdiffference_chroma: (a: number, b: number) => void;
readonly __wbg_get_hctdiffference_lightness: (a: number) => number;
readonly __wbg_set_hctdiffference_lightness: (a: number, b: number) => void;
readonly __wbg_rgbdifference_free: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_r: (a: number) => number;
readonly __wbg_set_rgbdifference_r: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_g: (a: number) => number;
readonly __wbg_set_rgbdifference_g: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_b: (a: number) => number;
readonly __wbg_set_rgbdifference_b: (a: number, b: number) => void;
readonly __wbg_hsldifference_free: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_hue: (a: number) => number;
readonly __wbg_set_hsldifference_hue: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_saturation: (a: number) => number;
readonly __wbg_set_hsldifference_saturation: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_lightness: (a: number) => number;
readonly __wbg_set_hsldifference_lightness: (a: number, b: number) => void;
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_2: WebAssembly.Table;

View File

@ -739,6 +739,421 @@ export function search_color_cards(tag, category) {
return takeFromExternrefTable0(ret[0]);
}
/**
* @param {string} color
* @param {string} other
* @returns {RGBDifference}
*/
export function differ_in_rgb(color, other) {
const ptr0 = passStringToWasm0(color, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(other, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.differ_in_rgb(ptr0, len0, ptr1, len1);
if (ret[2]) {
throw takeFromExternrefTable0(ret[1]);
}
return RGBDifference.__wrap(ret[0]);
}
/**
* @param {string} color
* @param {string} other
* @returns {HSLDifference}
*/
export function differ_in_hsl(color, other) {
const ptr0 = passStringToWasm0(color, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(other, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.differ_in_hsl(ptr0, len0, ptr1, len1);
if (ret[2]) {
throw takeFromExternrefTable0(ret[1]);
}
return HSLDifference.__wrap(ret[0]);
}
/**
* @param {string} color
* @param {string} other
* @returns {HctDiffference}
*/
export function differ_in_hct(color, other) {
const ptr0 = passStringToWasm0(color, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(other, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.differ_in_hct(ptr0, len0, ptr1, len1);
if (ret[2]) {
throw takeFromExternrefTable0(ret[1]);
}
return HctDiffference.__wrap(ret[0]);
}
/**
* @param {string} color
* @param {string} other
* @returns {OklchDifference}
*/
export function differ_in_oklch(color, other) {
const ptr0 = passStringToWasm0(color, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(other, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.differ_in_oklch(ptr0, len0, ptr1, len1);
if (ret[2]) {
throw takeFromExternrefTable0(ret[1]);
}
return OklchDifference.__wrap(ret[0]);
}
function _assertClass(instance, klass) {
if (!(instance instanceof klass)) {
throw new Error(`expected instance of ${klass.name}`);
}
}
const DifferFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_differ_free(ptr >>> 0, 1));
export class Differ {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(Differ.prototype);
obj.__wbg_ptr = ptr;
DifferFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
DifferFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_differ_free(ptr, 0);
}
/**
* @returns {number}
*/
get delta() {
const ret = wasm.__wbg_get_differ_delta(this.__wbg_ptr);
return ret;
}
/**
* @param {number} arg0
*/
set delta(arg0) {
wasm.__wbg_set_differ_delta(this.__wbg_ptr, arg0);
}
/**
* @returns {number}
*/
get percent() {
const ret = wasm.__wbg_get_differ_percent(this.__wbg_ptr);
return ret;
}
/**
* @param {number} arg0
*/
set percent(arg0) {
wasm.__wbg_set_differ_percent(this.__wbg_ptr, arg0);
}
}
const HSLDifferenceFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_hsldifference_free(ptr >>> 0, 1));
export class HSLDifference {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(HSLDifference.prototype);
obj.__wbg_ptr = ptr;
HSLDifferenceFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
HSLDifferenceFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_hsldifference_free(ptr, 0);
}
/**
* @returns {Differ}
*/
get hue() {
const ret = wasm.__wbg_get_hsldifference_hue(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set hue(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_hsldifference_hue(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get saturation() {
const ret = wasm.__wbg_get_hsldifference_saturation(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set saturation(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_hsldifference_saturation(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get lightness() {
const ret = wasm.__wbg_get_hsldifference_lightness(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set lightness(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_hsldifference_lightness(this.__wbg_ptr, ptr0);
}
}
const HctDiffferenceFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_hctdiffference_free(ptr >>> 0, 1));
export class HctDiffference {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(HctDiffference.prototype);
obj.__wbg_ptr = ptr;
HctDiffferenceFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
HctDiffferenceFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_hctdiffference_free(ptr, 0);
}
/**
* @returns {Differ}
*/
get hue() {
const ret = wasm.__wbg_get_hctdiffference_hue(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set hue(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_hctdiffference_hue(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get chroma() {
const ret = wasm.__wbg_get_hctdiffference_chroma(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set chroma(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_hctdiffference_chroma(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get lightness() {
const ret = wasm.__wbg_get_hctdiffference_lightness(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set lightness(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_hctdiffference_lightness(this.__wbg_ptr, ptr0);
}
}
const OklchDifferenceFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_oklchdifference_free(ptr >>> 0, 1));
export class OklchDifference {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(OklchDifference.prototype);
obj.__wbg_ptr = ptr;
OklchDifferenceFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
OklchDifferenceFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_oklchdifference_free(ptr, 0);
}
/**
* @returns {Differ}
*/
get hue() {
const ret = wasm.__wbg_get_oklchdifference_hue(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set hue(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_oklchdifference_hue(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get chroma() {
const ret = wasm.__wbg_get_oklchdifference_chroma(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set chroma(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_oklchdifference_chroma(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get lightness() {
const ret = wasm.__wbg_get_oklchdifference_lightness(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set lightness(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_oklchdifference_lightness(this.__wbg_ptr, ptr0);
}
}
const RGBDifferenceFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_rgbdifference_free(ptr >>> 0, 1));
export class RGBDifference {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(RGBDifference.prototype);
obj.__wbg_ptr = ptr;
RGBDifferenceFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
RGBDifferenceFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_rgbdifference_free(ptr, 0);
}
/**
* @returns {Differ}
*/
get r() {
const ret = wasm.__wbg_get_rgbdifference_r(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set r(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_rgbdifference_r(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get g() {
const ret = wasm.__wbg_get_rgbdifference_g(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set g(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_rgbdifference_g(this.__wbg_ptr, ptr0);
}
/**
* @returns {Differ}
*/
get b() {
const ret = wasm.__wbg_get_rgbdifference_b(this.__wbg_ptr);
return Differ.__wrap(ret);
}
/**
* @param {Differ} arg0
*/
set b(arg0) {
_assertClass(arg0, Differ);
var ptr0 = arg0.__destroy_into_raw();
wasm.__wbg_set_rgbdifference_b(this.__wbg_ptr, ptr0);
}
}
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {

View File

@ -31,6 +31,43 @@ export const tonal_lighten_series: (a: number, b: number, c: number, d: number)
export const tonal_darken_series: (a: number, b: number, c: number, d: number) => [number, number, number, number];
export const color_categories: () => [number, number, number];
export const search_color_cards: (a: number, b: number, c: number, d: number) => [number, number, number];
export const differ_in_rgb: (a: number, b: number, c: number, d: number) => [number, number, number];
export const differ_in_hsl: (a: number, b: number, c: number, d: number) => [number, number, number];
export const differ_in_hct: (a: number, b: number, c: number, d: number) => [number, number, number];
export const differ_in_oklch: (a: number, b: number, c: number, d: number) => [number, number, number];
export const __wbg_oklchdifference_free: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_hue: (a: number) => number;
export const __wbg_set_oklchdifference_hue: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_chroma: (a: number) => number;
export const __wbg_set_oklchdifference_chroma: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_lightness: (a: number) => number;
export const __wbg_set_oklchdifference_lightness: (a: number, b: number) => void;
export const __wbg_differ_free: (a: number, b: number) => void;
export const __wbg_get_differ_delta: (a: number) => number;
export const __wbg_set_differ_delta: (a: number, b: number) => void;
export const __wbg_get_differ_percent: (a: number) => number;
export const __wbg_set_differ_percent: (a: number, b: number) => void;
export const __wbg_hctdiffference_free: (a: number, b: number) => void;
export const __wbg_get_hctdiffference_hue: (a: number) => number;
export const __wbg_set_hctdiffference_hue: (a: number, b: number) => void;
export const __wbg_get_hctdiffference_chroma: (a: number) => number;
export const __wbg_set_hctdiffference_chroma: (a: number, b: number) => void;
export const __wbg_get_hctdiffference_lightness: (a: number) => number;
export const __wbg_set_hctdiffference_lightness: (a: number, b: number) => void;
export const __wbg_rgbdifference_free: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_r: (a: number) => number;
export const __wbg_set_rgbdifference_r: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_g: (a: number) => number;
export const __wbg_set_rgbdifference_g: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_b: (a: number) => number;
export const __wbg_set_rgbdifference_b: (a: number, b: number) => void;
export const __wbg_hsldifference_free: (a: number, b: number) => void;
export const __wbg_get_hsldifference_hue: (a: number) => number;
export const __wbg_set_hsldifference_hue: (a: number, b: number) => void;
export const __wbg_get_hsldifference_saturation: (a: number) => number;
export const __wbg_set_hsldifference_saturation: (a: number, b: number) => void;
export const __wbg_get_hsldifference_lightness: (a: number) => number;
export const __wbg_set_hsldifference_lightness: (a: number, b: number) => void;
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export_2: WebAssembly.Table;

View File

@ -1,5 +1,6 @@
@layer components {
.color_picker {
min-width: calc(var(--spacing) * 105);
max-width: calc(var(--spacing) * 125);
display: flex;
flex-direction: column;

16
src/material-2-scheme.ts Normal file
View File

@ -0,0 +1,16 @@
import { SchemeColor } from './models';
export type SchemeSet = {
primary: SchemeColor;
primaryVariant: SchemeColor;
onPrimary: SchemeColor;
secondary: SchemeColor;
secondaryVariant: SchemeColor;
onSecondary: SchemeColor;
background: SchemeColor;
onBackground: SchemeColor;
surface: SchemeColor;
onSurface: SchemeColor;
error: SchemeColor;
onError: SchemeColor;
};

49
src/material-3-scheme.ts Normal file
View File

@ -0,0 +1,49 @@
import { SchemeColor } from './models';
export type SchemeSet = {
primary: SchemeColor;
onPrimary: SchemeColor;
primaryContainer: SchemeColor;
onPrimaryContainer: SchemeColor;
primaryFixed: SchemeColor;
onPrimaryFixed: SchemeColor;
primaryFixedDim: SchemeColor;
onPrimaryFixedVaraint: SchemeColor;
secondary: SchemeColor;
onSecondary: SchemeColor;
secondaryContainer: SchemeColor;
onSecondaryContainer: SchemeColor;
secondaryFixed: SchemeColor;
onSecondaryFixed: SchemeColor;
secondaryFixedDim: SchemeColor;
onSecondaryFixedVaraint: SchemeColor;
tertiary: SchemeColor;
onTertiary: SchemeColor;
tertiaryContainer: SchemeColor;
onTertiaryContainer: SchemeColor;
tertiaryFixed: SchemeColor;
onTertiaryFixed: SchemeColor;
tertiaryFixedDim: SchemeColor;
onTertiaryFixedVaraint: SchemeColor;
error: SchemeColor;
onError: SchemeColor;
errorContainer: SchemeColor;
onErrorContainer: SchemeColor;
surface: SchemeColor;
surfaceDim: SchemeColor;
surfaceBright: SchemeColor;
onSurface: SchemeColor;
onSurfaceVariant: SchemeColor;
surfaceContainerLowest: SchemeColor;
surfaceContainerLow: SchemeColor;
surfaceContainer: SchemeColor;
surfaceContainerHigh: SchemeColor;
surfaceContainerHighest: SchemeColor;
inverseSurface: SchemeColor;
onInverseSurface: SchemeColor;
inversePrimary: SchemeColor;
outline: SchemeColor;
outlineVariant: SchemeColor;
scrim: SchemeColor;
shadow: SchemeColor;
};

View File

@ -20,3 +20,15 @@ export type ColorDescription = {
lab: [number, number, number];
oklch: [number, number, number];
};
export type SchemeType = 'color_q' | 'material_2' | 'material_3';
export type SchemeColor = string | null;
export type SchemeContent<Scheme> = {
id: string;
name: string;
createdAt: string;
description: string | null;
type: SchemeType;
lightScheme: Scheme;
darkScheme: Scheme;
};

View 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);
}
}
}
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View File

@ -0,0 +1,4 @@
export type CompareMethodProps = {
basic?: string;
compare?: string;
};

View 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
View 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>
);
}

View File

@ -6,9 +6,8 @@
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: flex-start;
gap: var(--spacing-m);
gap: var(--spacing-l);
.mixer_column {
display: flex;
flex-direction: column;