增加内置的颜色色卡支持。
This commit is contained in:
parent
5e7b1e709d
commit
f2031f3d8c
89
color-module/src/color_card.rs
Normal file
89
color-module/src/color_card.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use std::sync::LazyLock;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{Display, EnumIter, EnumString};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ColorDescription {
|
||||
pub name: String,
|
||||
pub pinyin: Vec<String>,
|
||||
pub hue: f32,
|
||||
pub lightness: f32,
|
||||
pub category: String,
|
||||
pub tags: Vec<String>,
|
||||
pub rgb: [u8; 3],
|
||||
pub hsl: [f32; 3],
|
||||
pub lab: [f32; 3],
|
||||
pub oklch: [f32; 3],
|
||||
}
|
||||
|
||||
const COLOR_CARDS_JSON: &str = include_str!("colorcards.json");
|
||||
pub const COLOR_CARDS: LazyLock<Vec<ColorDescription>> =
|
||||
LazyLock::new(|| serde_json::from_str(COLOR_CARDS_JSON).expect("Failed to parse color cards"));
|
||||
const CHROMA_EPSILON: f32 = 0.0001;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Display, EnumString, EnumIter)]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum Category {
|
||||
Red,
|
||||
Orange,
|
||||
Yellow,
|
||||
Green,
|
||||
Cyan,
|
||||
Blue,
|
||||
Purple,
|
||||
Magenta,
|
||||
White,
|
||||
Black,
|
||||
Gray,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Category {
|
||||
pub fn from_oklch_components(lightness: f32, chroma: f32, hue: f32) -> Self {
|
||||
if chroma < CHROMA_EPSILON {
|
||||
if lightness < 0.1 {
|
||||
Category::Black
|
||||
} else if lightness > 0.9 {
|
||||
Category::White
|
||||
} else {
|
||||
Category::Gray
|
||||
}
|
||||
} else {
|
||||
let processed_hue = hue % 360.0;
|
||||
match processed_hue {
|
||||
0.0..=30.0 => Category::Red,
|
||||
30.0..=60.0 => Category::Orange,
|
||||
60.0..=90.0 => Category::Yellow,
|
||||
90.0..=150.0 => Category::Green,
|
||||
150.0..=210.0 => Category::Cyan,
|
||||
210.0..=270.0 => Category::Blue,
|
||||
270.0..=300.0 => Category::Purple,
|
||||
300.0..=330.0 => Category::Magenta,
|
||||
330.0..=360.0 => Category::Red,
|
||||
_ => Category::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_oklch(oklch: &[f32; 3]) -> Self {
|
||||
Category::from_oklch_components(oklch[0], oklch[1], oklch[2])
|
||||
}
|
||||
|
||||
pub fn label(&self) -> String {
|
||||
match self {
|
||||
Category::Red => "Red".to_string(),
|
||||
Category::Orange => "Orange".to_string(),
|
||||
Category::Yellow => "Yellow".to_string(),
|
||||
Category::Green => "Green".to_string(),
|
||||
Category::Cyan => "Cyan".to_string(),
|
||||
Category::Blue => "Blue".to_string(),
|
||||
Category::Purple => "Purple".to_string(),
|
||||
Category::Magenta => "Magenta".to_string(),
|
||||
Category::White => "White".to_string(),
|
||||
Category::Black => "Black".to_string(),
|
||||
Category::Gray => "Gray".to_string(),
|
||||
Category::Unknown => "Unknown".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
1
color-module/src/colorcards.json
Normal file
1
color-module/src/colorcards.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,6 @@
|
|||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use color_card::Category;
|
||||
use palette::{
|
||||
cam16::{Cam16Jch, Parameters},
|
||||
color_difference::Wcag21RelativeContrast,
|
||||
|
@ -7,8 +8,10 @@ use palette::{
|
|||
convert::FromColorUnclamped,
|
||||
Darken, FromColor, Hsl, IntoColor, IsWithinBounds, Lab, Lighten, Mix, Oklch, ShiftHue, Srgb,
|
||||
};
|
||||
use strum::IntoEnumIterator;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
mod color_card;
|
||||
mod errors;
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -377,3 +380,45 @@ pub fn tonal_darken_series(
|
|||
|
||||
Ok(color_series.into_iter().rev().collect())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn color_categories() -> Result<JsValue, String> {
|
||||
let categories = Category::iter()
|
||||
.map(|variant| {
|
||||
serde_json::json!({
|
||||
"label": variant.label(),
|
||||
"value": variant.to_string(),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
serde_wasm_bindgen::to_value(&categories).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn search_color_cards(tag: String, category: Option<String>) -> Result<JsValue, String> {
|
||||
let selected_category = category.and_then(|c| Category::from_str(&c).ok());
|
||||
let all_cards = &*color_card::COLOR_CARDS;
|
||||
let mut cards = all_cards
|
||||
.iter()
|
||||
.filter(|card| card.tags.contains(&tag))
|
||||
.filter(|card| {
|
||||
if let Some(category) = &selected_category {
|
||||
let card_category = Category::from_oklch(&card.oklch);
|
||||
card_category == *category
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cards.sort_by(|a, b| {
|
||||
a.oklch[2]
|
||||
.partial_cmp(&b.oklch[2])
|
||||
.or_else(|| a.oklch[1].partial_cmp(&b.oklch[1]))
|
||||
.or_else(|| a.oklch[0].partial_cmp(&b.oklch[0]))
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
|
||||
serde_wasm_bindgen::to_value(&cards).map_err(|e| e.to_string())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user