From 60eb4dbb567648600e0684b9f6a7bf3fb2de88be Mon Sep 17 00:00:00 2001
From: luoxiaozero <48741584+luoxiaozero@users.noreply.github.com>
Date: Wed, 7 Feb 2024 21:42:34 +0800
Subject: [PATCH] feat: rewrite color (#98)
---
demo/Cargo.toml | 1 +
demo_markdown/docs/color_picker/mod.md | 32 ++++-
thaw/Cargo.toml | 1 +
thaw/src/color_picker/color.rs | 162 +++----------------------
thaw/src/color_picker/mod.rs | 85 +++++++++----
5 files changed, 110 insertions(+), 171 deletions(-)
diff --git a/demo/Cargo.toml b/demo/Cargo.toml
index b614128..2aa85f7 100644
--- a/demo/Cargo.toml
+++ b/demo/Cargo.toml
@@ -14,6 +14,7 @@ leptos_devtools = { version = "0.0.1", optional = true}
thaw = { path = "../thaw" }
demo_markdown = { path = "../demo_markdown" }
icondata = "0.3.0"
+palette = "0.7.4"
[features]
default = ["csr"]
diff --git a/demo_markdown/docs/color_picker/mod.md b/demo_markdown/docs/color_picker/mod.md
index 39b9fba..6739e89 100644
--- a/demo_markdown/docs/color_picker/mod.md
+++ b/demo_markdown/docs/color_picker/mod.md
@@ -1,16 +1,38 @@
# Color Picker
```rust demo
-let value = create_rw_signal(RGBA::default());
+use palette::Srgb;
+
+let value = create_rw_signal(Color::from(Srgb::new(0.0, 0.0, 0.0)));
view! {
}
```
+### Color Format
+
+Encoding formats, support RGB, HSV, HSL.
+
+```rust demo
+use palette::{Hsl, Hsv, Srgb};
+
+let rgb = create_rw_signal(Color::from(Srgb::new(0.0, 0.0, 0.0)));
+let hsv = create_rw_signal(Color::from(Hsv::new(0.0, 0.0, 0.0)));
+let hsl = create_rw_signal(Color::from(Hsl::new(0.0, 0.0, 0.0)));
+
+view! {
+
+
+
+
+
+}
+```
+
### DatePicker Props
-| Name | Type | Default | Desciption |
-| ----- | --------------------- | -------------------- | ----------------------------------------------- |
-| class | `MaybeSignal` | `Default::default()` | Addtional classes for the color picker element. |
-| value | `RwSignal` | `Default::default()` | Value of the picker. |
+| Name | Type | Default | Desciption |
+| ----- | ----------------------------------- | -------------------- | ----------------------------------------------- |
+| class | `OptionalProp>` | `Default::default()` | Addtional classes for the color picker element. |
+| value | `Model` | `Default::default()` | Value of the picker. |
diff --git a/thaw/Cargo.toml b/thaw/Cargo.toml
index f192742..6432c4c 100644
--- a/thaw/Cargo.toml
+++ b/thaw/Cargo.toml
@@ -27,6 +27,7 @@ icondata_ai = "0.0.10"
uuid = { version = "1.7.0", features = ["v4"] }
cfg-if = "1.0.0"
chrono = "0.4.33"
+palette = "0.7.4"
[features]
csr = ["leptos/csr"]
diff --git a/thaw/src/color_picker/color.rs b/thaw/src/color_picker/color.rs
index 43b5a3d..a999b9e 100644
--- a/thaw/src/color_picker/color.rs
+++ b/thaw/src/color_picker/color.rs
@@ -1,160 +1,32 @@
+use palette::{Hsl, Hsv, Srgb};
+
#[derive(Clone)]
-pub struct RGBA {
- pub red: u8,
- pub green: u8,
- pub blue: u8,
- pub alpha: u8,
+pub enum Color {
+ RGB(Srgb),
+ HSV(Hsv),
+ HSL(Hsl),
}
-impl Default for RGBA {
+impl Default for Color {
fn default() -> Self {
- Self {
- red: Default::default(),
- green: Default::default(),
- blue: Default::default(),
- alpha: u8::MAX,
- }
+ Self::RGB(Srgb::new(0.0, 0.0, 0.0))
}
}
-impl RGBA {
- pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
- Self {
- red: r,
- green: g,
- blue: b,
- alpha: a,
- }
- }
-
- pub fn new_rgb(r: u8, g: u8, b: u8) -> Self {
- Self {
- red: r,
- green: g,
- blue: b,
- alpha: u8::MAX,
- }
- }
-
- pub fn to_hex_string(&self) -> String {
- if self.alpha == u8::MAX {
- format!("#{:02X}{:02X}{:02X}", self.red, self.green, self.blue)
- } else {
- format!(
- "#{:02X}{:02X}{:02X}{:02X}",
- self.red, self.green, self.blue, self.alpha
- )
- }
+impl From for Color {
+ fn from(value: Srgb) -> Self {
+ Self::RGB(value)
}
}
-impl From for RGBA {
- fn from(value: HSV) -> Self {
- let HSV {
- hue: h,
- saturation: s,
- value: v,
- alpha,
- } = value;
- let h = f64::from(h);
-
- let c = v * s;
- let x = c * (1.0 - f64::abs(((h / 60.0) % 2.0) - 1.0));
- let m = v - c;
-
- let (r, g, b) = if (0.0..60.0).contains(&h) {
- (c, x, 0.0)
- } else if (60.0..120.0).contains(&h) {
- (x, c, 0.0)
- } else if (120.0..180.0).contains(&h) {
- (0.0, c, x)
- } else if (180.0..240.0).contains(&h) {
- (0.0, x, c)
- } else if (240.0..300.0).contains(&h) {
- (x, 0.0, c)
- } else if (300.0..360.0).contains(&h) {
- (c, 0.0, x)
- } else {
- (c, x, 0.0)
- };
-
- let (r, g, b) = (
- ((r + m) * 255.0) as u8,
- ((g + m) * 255.0) as u8,
- ((b + m) * 255.0) as u8,
- );
-
- RGBA::new(r, g, b, alpha)
+impl From for Color {
+ fn from(value: Hsv) -> Self {
+ Self::HSV(value)
}
}
-#[derive(Clone)]
-pub struct HSV {
- pub hue: u16,
- pub saturation: f64,
- pub value: f64,
- pub alpha: u8,
-}
-
-impl HSV {
- pub fn new(hue: u16, saturation: f64, value: f64) -> Self {
- Self {
- hue,
- saturation,
- value,
- alpha: u8::MAX,
- }
- }
-
- pub fn new_alpha(hue: u16, saturation: f64, value: f64, alpha: u8) -> Self {
- Self {
- hue,
- saturation,
- value,
- alpha,
- }
- }
-}
-
-impl From for HSV {
- fn from(value: RGBA) -> Self {
- let RGBA {
- red: r,
- green: g,
- blue: b,
- alpha,
- } = value;
-
- let (r, g, b) = (r as f64 / 255.0, g as f64 / 255.0, b as f64 / 255.0);
-
- let c_max = f64::max(r, f64::max(g, b));
- let c_min = f64::min(r, f64::min(g, b));
- let delta = c_max - c_min;
-
- let hue = if delta == 0.0 {
- 0.0
- } else if c_max == r {
- 60.0 * (((g - b) / delta) % 6.0)
- } else if c_max == g {
- 60.0 * (((b - r) / delta) + 2.0)
- } else if c_max == b {
- 60.0 * (((r - g) / delta) + 4.0)
- } else {
- unreachable!()
- };
-
- let saturation = match c_max == 0.0 {
- true => 0.0,
- false => delta / c_max,
- };
-
- let value = c_max;
-
- HSV {
- hue: hue.to_string().parse().unwrap(),
- saturation,
- value,
- alpha,
- }
+impl From for Color {
+ fn from(value: Hsl) -> Self {
+ Self::HSL(value)
}
}
diff --git a/thaw/src/color_picker/mod.rs b/thaw/src/color_picker/mod.rs
index 076a81f..3fb09e0 100644
--- a/thaw/src/color_picker/mod.rs
+++ b/thaw/src/color_picker/mod.rs
@@ -1,20 +1,22 @@
mod color;
mod theme;
+pub use color::*;
+pub use theme::ColorPickerTheme;
+
use crate::{
components::{Binder, Follower, FollowerPlacement},
use_theme,
utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme,
};
-pub use color::*;
use leptos::leptos_dom::helpers::WindowListenerHandle;
use leptos::*;
-pub use theme::ColorPickerTheme;
+use palette::{Hsv, IntoColor, Srgb};
#[component]
pub fn ColorPicker(
- #[prop(optional, into)] value: Model,
+ #[prop(optional, into)] value: Model,
#[prop(optional, into)] class: OptionalProp>,
) -> impl IntoView {
mount_style("color-picker", include_str!("./color-picker.css"));
@@ -28,22 +30,51 @@ pub fn ColorPicker(
})
});
- let hue = create_rw_signal(0);
- let sv = create_rw_signal((0.0, 0.0));
+ let hue = create_rw_signal(0f32);
+ let sv = create_rw_signal((0f32, 0f32));
let label = create_rw_signal(String::new());
let style = create_memo(move |_| {
let mut style = String::new();
- value.with(|value| {
- let value = value.to_hex_string();
- style.push_str(&format!("background-color: {value};"));
+ value.with(|color| {
let (s, v) = sv.get_untracked();
if s < 0.5 && v > 0.5 {
style.push_str("color: #000;");
} else {
style.push_str("color: #fff;");
}
- label.set(value);
+ match color {
+ Color::RGB(rgb) => {
+ let rgb = Srgb::::from_format(rgb.clone());
+ let color = format!("rgb({}, {}, {})", rgb.red, rgb.green, rgb.blue);
+ style.push_str(&format!("background-color: {color};"));
+ label.set(color);
+ }
+ Color::HSV(hsv) => {
+ let rgb: Srgb = hsv.clone().into_color();
+ let rgb = Srgb::::from_format(rgb);
+ let color = format!("rgb({}, {}, {})", rgb.red, rgb.green, rgb.blue);
+ style.push_str(&format!("background-color: {color};"));
+
+ let color = format!(
+ "hsv({}, {:.0}%, {:.0}%)",
+ hsv.hue.into_inner(),
+ hsv.saturation * 100.0,
+ hsv.value * 100.0
+ );
+ label.set(color);
+ }
+ Color::HSL(hsl) => {
+ let color = format!(
+ "hsl({}, {:.0}%, {:.0}%)",
+ hsl.hue.into_inner(),
+ hsl.saturation * 100.0,
+ hsl.lightness * 100.0
+ );
+ style.push_str(&format!("background-color: {color};"));
+ label.set(color);
+ }
+ }
});
style
@@ -53,16 +84,28 @@ pub fn ColorPicker(
let (s, v) = sv.get();
let hue_value = hue.get();
if prev.is_none() {
- let HSV {
+ let hsv = match value.get_untracked() {
+ Color::RGB(rgb) => rgb.into_color(),
+ Color::HSV(hsv) => hsv,
+ Color::HSL(hsl) => hsl.into_color(),
+ };
+ let Hsv {
hue: h,
saturation: s,
value: v,
..
- } = value.get_untracked().into();
- hue.set(h);
- sv.set((s, v))
+ } = hsv;
+ hue.set(h.into_inner());
+ sv.set((s.into(), v.into()))
} else {
- value.set(RGBA::from(HSV::new(hue_value, s, v)));
+ value.update(|color| {
+ let new_hsv: Hsv = Hsv::new(hue_value, s, v);
+ match color {
+ Color::RGB(rgb) => *rgb = new_hsv.into_color(),
+ Color::HSV(hsv) => *hsv = new_hsv,
+ Color::HSL(hsl) => *hsl = new_hsv.into_color(),
+ }
+ });
}
});
@@ -124,7 +167,7 @@ pub fn ColorPicker(
}
#[component]
-fn ColorPanel(hue: ReadSignal, sv: RwSignal<(f64, f64)>) -> impl IntoView {
+fn ColorPanel(hue: ReadSignal, sv: RwSignal<(f32, f32)>) -> impl IntoView {
let panel_ref = create_node_ref::();
let mouse = store_value(Vec::::new());
@@ -143,14 +186,14 @@ fn ColorPanel(hue: ReadSignal, sv: RwSignal<(f64, f64)>) -> impl IntoView {
} else if v < 0.0 {
0.0
} else {
- v
+ format!("{:.2}", v).parse::().unwrap()
};
let s = if s > 1.0 {
1.0
} else if s < 0.0 {
0.0
} else {
- s
+ format!("{:.2}", s).parse::().unwrap()
};
sv.set((s, v))
@@ -197,7 +240,7 @@ fn ColorPanel(hue: ReadSignal, sv: RwSignal<(f64, f64)>) -> impl IntoView {
}
#[component]
-fn HueSlider(hue: RwSignal) -> impl IntoView {
+fn HueSlider(hue: RwSignal) -> impl IntoView {
let rail_ref = create_node_ref::();
let mouse = store_value(Vec::::new());
@@ -208,11 +251,11 @@ fn HueSlider(hue: RwSignal) -> impl IntoView {
let ev_x = f64::from(ev.x());
let value = (ev_x - rect.left() - 6.0) / (rect.width() - 12.0) * 359.0;
let value = if value < 0.0 {
- 0
+ 0.0
} else if value > 359.0 {
- 359
+ 359.0
} else {
- value.round().to_string().parse::().unwrap()
+ value.round().to_string().parse::().unwrap()
};
hue.set(value);
}