增加Swatch Scheme主题的生成和导出。
This commit is contained in:
		| @@ -70,6 +70,13 @@ macro_rules! parse_to_oklch { | ||||
|                 .into_format::<f32>(), | ||||
|         ) | ||||
|     }; | ||||
|     ($origin: expr) => { | ||||
|         palette::Oklch::from_color( | ||||
|             palette::Srgb::from_str($origin) | ||||
|                 .map_err(|_| crate::errors::ColorError::UnrecogniazedRGB($origin.to_string()))? | ||||
|                 .into_format::<f32>(), | ||||
|         ) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
|   | ||||
| @@ -3,6 +3,7 @@ use std::collections::HashMap; | ||||
| use material_design_2::MaterialDesign2Scheme; | ||||
| use material_design_3::MaterialDesign3Scheme; | ||||
| use q_style::{QScheme, SchemeSetting}; | ||||
| use swatch_style::{SwatchEntry, SwatchSchemeSetting}; | ||||
| use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; | ||||
|  | ||||
| use crate::errors; | ||||
| @@ -126,3 +127,18 @@ pub fn generate_q_scheme_manually( | ||||
|     )) | ||||
|     .map_err(|_| errors::ColorError::UnableToAssembleOutput)?) | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| pub fn generate_swatch_scheme( | ||||
|     colors: Vec<SwatchEntry>, | ||||
|     setting: SwatchSchemeSetting, | ||||
| ) -> Result<JsValue, errors::ColorError> { | ||||
|     let scheme = swatch_style::SwatchScheme::new(colors, setting)?; | ||||
|     Ok(serde_wasm_bindgen::to_value(&( | ||||
|         scheme.swatches(), | ||||
|         scheme.output_css_variables(), | ||||
|         scheme.output_scss_variables(), | ||||
|         scheme.output_javascript_object(), | ||||
|     )) | ||||
|     .map_err(|_| errors::ColorError::UnableToAssembleOutput)?) | ||||
| } | ||||
|   | ||||
							
								
								
									
										122
									
								
								color-module/src/schemes/swatch_style/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								color-module/src/schemes/swatch_style/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| use palette::FromColor; | ||||
| use std::collections::HashMap; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| pub use setting::SwatchSchemeSetting; | ||||
| use swatch::Swatch; | ||||
| use wasm_bindgen::prelude::*; | ||||
|  | ||||
| use crate::{errors, parse_to_oklch}; | ||||
|  | ||||
| use super::SchemeExport; | ||||
|  | ||||
| mod setting; | ||||
| mod swatch; | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct SwatchScheme { | ||||
|     light: HashMap<String, Swatch>, | ||||
|     dark: HashMap<String, Swatch>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| #[wasm_bindgen(getter_with_clone)] | ||||
| pub struct SwatchEntry { | ||||
|     pub name: String, | ||||
|     pub color: String, | ||||
| } | ||||
|  | ||||
| impl SwatchScheme { | ||||
|     pub fn new( | ||||
|         colors: Vec<SwatchEntry>, | ||||
|         setting: SwatchSchemeSetting, | ||||
|     ) -> Result<Self, errors::ColorError> { | ||||
|         let mut light = HashMap::new(); | ||||
|         let mut dark = HashMap::new(); | ||||
|  | ||||
|         for entry in colors { | ||||
|             let color = parse_to_oklch!(&entry.color); | ||||
|             let darken_color = color * setting.dark_convert; | ||||
|             light.insert(entry.name.clone(), Swatch::new(&color, &setting)); | ||||
|             dark.insert(entry.name, Swatch::new(&darken_color, &setting)); | ||||
|         } | ||||
|  | ||||
|         Ok(Self { light, dark }) | ||||
|     } | ||||
|  | ||||
|     pub fn swatches(&self) -> HashMap<String, HashMap<String, Vec<String>>> { | ||||
|         let mut light_swatches = HashMap::new(); | ||||
|         let mut dark_swatches = HashMap::new(); | ||||
|  | ||||
|         for (name, swatch) in &self.light { | ||||
|             light_swatches.insert(name.clone(), swatch.swtch_hex()); | ||||
|         } | ||||
|         for (name, swatch) in &self.dark { | ||||
|             dark_swatches.insert(name.clone(), swatch.swtch_hex()); | ||||
|         } | ||||
|  | ||||
|         HashMap::from([ | ||||
|             ("light".to_string(), light_swatches), | ||||
|             ("dark".to_string(), dark_swatches), | ||||
|         ]) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl SchemeExport for SwatchScheme { | ||||
|     fn output_css_variables(&self) -> String { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         for (name, swatch) in &self.light { | ||||
|             variables.extend(swatch.to_css_variables("light", name)); | ||||
|         } | ||||
|         for (name, swatch) in &self.dark { | ||||
|             variables.extend(swatch.to_css_variables("dark", name)); | ||||
|         } | ||||
|  | ||||
|         variables.join("\n") | ||||
|     } | ||||
|  | ||||
|     fn output_scss_variables(&self) -> String { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         for (name, swatch) in &self.light { | ||||
|             variables.extend(swatch.to_scss_variables("light", name)); | ||||
|         } | ||||
|         for (name, swatch) in &self.dark { | ||||
|             variables.extend(swatch.to_scss_variables("dark", name)); | ||||
|         } | ||||
|  | ||||
|         variables.join("\n") | ||||
|     } | ||||
|  | ||||
|     fn output_javascript_object(&self) -> String { | ||||
|         let mut object = Vec::new(); | ||||
|         object.push("{".to_string()); | ||||
|  | ||||
|         object.push("  light: {".to_string()); | ||||
|         for (name, swatch) in &self.light { | ||||
|             object.extend( | ||||
|                 swatch | ||||
|                     .to_javascript_fields("light", name) | ||||
|                     .iter() | ||||
|                     .map(|s| format!("    {}", s)), | ||||
|             ); | ||||
|         } | ||||
|         object.push("  },".to_string()); | ||||
|  | ||||
|         object.push("  dark: {".to_string()); | ||||
|         for (name, swatch) in &self.dark { | ||||
|             object.extend( | ||||
|                 swatch | ||||
|                     .to_javascript_fields("dark", name) | ||||
|                     .iter() | ||||
|                     .map(|s| format!("    {}", s)), | ||||
|             ); | ||||
|         } | ||||
|         object.push("  },".to_string()); | ||||
|  | ||||
|         object.push("}".to_string()); | ||||
|  | ||||
|         object.join("\n") | ||||
|     } | ||||
| } | ||||
							
								
								
									
										29
									
								
								color-module/src/schemes/swatch_style/setting.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								color-module/src/schemes/swatch_style/setting.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| use serde::Serialize; | ||||
| use wasm_bindgen::prelude::wasm_bindgen; | ||||
|  | ||||
| use crate::schemes::q_style::ColorShifting; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| #[wasm_bindgen] | ||||
| pub struct SwatchSchemeSetting { | ||||
|     pub amount: usize, | ||||
|     pub min_lightness: f32, | ||||
|     pub max_lightness: f32, | ||||
|     pub include_primary: bool, | ||||
|     pub dark_convert: ColorShifting, | ||||
| } | ||||
|  | ||||
| impl Default for SwatchSchemeSetting { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             amount: 10, | ||||
|             min_lightness: 10.0, | ||||
|             max_lightness: 90.0, | ||||
|             include_primary: false, | ||||
|             dark_convert: ColorShifting { | ||||
|                 chroma: -0.3, | ||||
|                 lightness: -0.3, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										133
									
								
								color-module/src/schemes/swatch_style/swatch.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								color-module/src/schemes/swatch_style/swatch.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| use palette::Oklch; | ||||
|  | ||||
| use crate::convert::map_oklch_to_srgb_hex; | ||||
|  | ||||
| use super::setting::SwatchSchemeSetting; | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Swatch { | ||||
|     min_key: f32, | ||||
|     max_key: f32, | ||||
|     primary_key: Oklch, | ||||
|     include_primary: bool, | ||||
|     color_amount: usize, | ||||
| } | ||||
|  | ||||
| impl Swatch { | ||||
|     pub fn new(primary: &Oklch, setting: &SwatchSchemeSetting) -> Self { | ||||
|         Self { | ||||
|             min_key: primary.l.min(setting.min_lightness), | ||||
|             max_key: primary.l.max(setting.max_lightness), | ||||
|             primary_key: primary.clone(), | ||||
|             include_primary: setting.include_primary, | ||||
|             color_amount: setting.amount, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn find_interval(&self) -> (usize, usize) { | ||||
|         if !self.include_primary { | ||||
|             return (0, 0); | ||||
|         } | ||||
|         if self.primary_key.l == self.min_key { | ||||
|             return (0, 1); | ||||
|         } | ||||
|         if self.primary_key.l == self.max_key { | ||||
|             return (self.color_amount - 2, self.color_amount - 1); | ||||
|         } | ||||
|         let step = (self.max_key - self.min_key) / (self.color_amount - 1) as f32; | ||||
|         let index = ((self.primary_key.l - self.min_key) / step) as usize; | ||||
|  | ||||
|         (index, index + 1) | ||||
|     } | ||||
|  | ||||
|     pub fn swatch(&self) -> Vec<Oklch> { | ||||
|         let mut swatch = Vec::new(); | ||||
|         if self.include_primary { | ||||
|             let (_, primary_index) = self.find_interval(); | ||||
|             if primary_index > 0 { | ||||
|                 let step = (self.max_key - self.min_key) / primary_index as f32; | ||||
|                 for i in 0..primary_index { | ||||
|                     let lightness = self.min_key + step * i as f32; | ||||
|                     swatch.push(Oklch { | ||||
|                         l: lightness, | ||||
|                         ..self.primary_key | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             if primary_index < self.color_amount - 1 { | ||||
|                 let step = | ||||
|                     (self.max_key - self.min_key) / (self.color_amount - primary_index) as f32; | ||||
|                 for i in primary_index..self.color_amount { | ||||
|                     let lightness = self.min_key + step * i as f32; | ||||
|                     swatch.push(Oklch { | ||||
|                         l: lightness, | ||||
|                         ..self.primary_key | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             let step = (self.max_key - self.min_key) / (self.color_amount - 1) as f32; | ||||
|             for i in 0..self.color_amount { | ||||
|                 let lightness = self.min_key + step * i as f32; | ||||
|                 swatch.push(Oklch { | ||||
|                     l: lightness, | ||||
|                     ..self.primary_key | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         swatch | ||||
|     } | ||||
|  | ||||
|     pub fn swtch_hex(&self) -> Vec<String> { | ||||
|         self.swatch().iter().map(map_oklch_to_srgb_hex).collect() | ||||
|     } | ||||
|  | ||||
|     pub fn to_css_variables(&self, prefix: &str, name: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|         for (i, color) in self.swatch().iter().enumerate() { | ||||
|             variables.push(format!( | ||||
|                 "--color-{}-{}-{}: #{};", | ||||
|                 prefix, | ||||
|                 name, | ||||
|                 i * 100, | ||||
|                 map_oklch_to_srgb_hex(color) | ||||
|             )); | ||||
|         } | ||||
|         variables | ||||
|     } | ||||
|  | ||||
|     pub fn to_scss_variables(&self, prefix: &str, name: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|         for (i, color) in self.swatch().iter().enumerate() { | ||||
|             variables.push(format!( | ||||
|                 "${}-{}-{}: #{};", | ||||
|                 prefix, | ||||
|                 name, | ||||
|                 i * 100, | ||||
|                 map_oklch_to_srgb_hex(color) | ||||
|             )); | ||||
|         } | ||||
|         variables | ||||
|     } | ||||
|  | ||||
|     pub fn to_javascript_fields(&self, prefix: &str, name: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|         let capitalized_name = name | ||||
|             .chars() | ||||
|             .next() | ||||
|             .unwrap() | ||||
|             .to_ascii_uppercase() | ||||
|             .to_string() | ||||
|             + &name[1..]; | ||||
|         for (i, color) in self.swatch().iter().enumerate() { | ||||
|             variables.push(format!( | ||||
|                 "{}{}{}: '#{}',", | ||||
|                 prefix, | ||||
|                 capitalized_name, | ||||
|                 i * 100, | ||||
|                 map_oklch_to_srgb_hex(color) | ||||
|             )); | ||||
|         } | ||||
|         variables | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user