floem plattle 调色板 - jiemo2187/rust-crates GitHub Wiki
代码来源:floem color_pallete
代码来源:floem webgpu
字体:fonts
本地运行:cargo run
网页运行: trunk serve
格式化配置rustfmt.toml
:
# https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=
# cargo +nightly fmt
imports_granularity = 'Item'
reorder_imports = true
group_imports = 'StdExternalCrate'
依赖Cargo.toml
:
[package]
name = "floem-palette"
version = "0.1.0"
edition = "2021"
[dependencies]
floem = "0.2.0"
im = "15.1.0"
palette = "0.7.6"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.7"
console_log = "1.0.0"
wasm-bindgen = "0.2.95"
wasm-bindgen-futures = "0.4.45"
web-sys = { version = "0.3.72", features = ["Element", "Window", "Document"] }
wgpu = "23.0.0"
main.rs
文件
use floem::peniko::Color;
use floem::prelude::Decorators;
use floem::prelude::RwSignal;
use floem::reactive::SignalGet;
use floem::reactive::SignalUpdate;
use floem::style::Position;
use floem::text::FONT_SYSTEM;
use floem::views::dyn_view;
use floem::views::empty;
use floem::views::label;
use floem::views::slider::Slider;
use floem::window::WindowConfig;
use floem::Application;
use floem::IntoView;
use palette::Hsl;
use palette::Hsv;
use palette::IntoColor;
use palette::Lch;
use palette::Srgb;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
const FIRA_MONO: &[u8] = include_bytes!("../fonts/FiraMono-Medium.ttf");
const FIRA_SANS: &[u8] = include_bytes!("../fonts/FiraSans-Medium.ttf");
const DEJAVU_SERIF: &[u8] = include_bytes!("../fonts/DejaVuSerif.ttf");
fn create_color_sliders(
label_text: &str,
signal: RwSignal<f32>,
update_fn: impl Fn(f32) + 'static,
) -> impl IntoView {
let text_label = label_text.to_string();
(
label(move || text_label.clone()).style(|s| s.size(65, 30)),
Slider::new(move || signal.get())
.on_change_px(move |new_value| update_fn(new_value as f32))
.style(|s| s.width(250)),
)
.style(|s| s.flex_row().gap(3).width_full())
}
fn create_color_display(
red: RwSignal<f32>,
green: RwSignal<f32>,
blue: RwSignal<f32>,
) -> impl IntoView {
(
dyn_view(move || {
format!(
"rgb: ({}, {}, {})",
(red.get() * 255.0) as i32,
(green.get() * 255.0) as i32,
(blue.get() * 255.0) as i32,
)
})
.style(|s| s.width(50).font_size(18)),
empty().style(move |s| {
s.background(Color::rgb(
red.get() as f64,
green.get() as f64,
blue.get() as f64,
))
.size(310, 75)
}),
)
.style(|s| s.flex_col().gap(10))
}
fn lch_view() -> impl IntoView {
let l = RwSignal::new(40.0);
let c = RwSignal::new(40.0);
let h = RwSignal::new(40.0);
let red = RwSignal::new(0.4);
let green = RwSignal::new(0.4);
let blue = RwSignal::new(0.4);
let update_rgb = move |get_values: Box<dyn Fn() -> (f32, f32, f32)>| {
let (l_val, c_val, h_val) = get_values();
let rgb: Srgb = Lch::new(l_val, c_val, h_val).into_color();
red.set(rgb.red);
green.set(rgb.green);
blue.set(rgb.blue);
};
(
(
create_color_sliders("Lightness", l, move |new_l| {
l.set(new_l);
update_rgb(Box::new(move || (new_l, c.get(), h.get())))
}),
create_color_sliders("Chroma", c, move |new_c| {
c.set(new_c);
update_rgb(Box::new(move || (l.get(), new_c, h.get())))
}),
create_color_sliders("Hue", h, move |new_h| {
h.set(new_h);
update_rgb(Box::new(move || (l.get(), c.get(), new_h)))
}),
)
.style(|s| s.flex_col().gap(3)),
create_color_display(red, green, blue),
)
.style(|s| s.flex_col().items_start().margin_right(10))
}
fn hsl_view() -> impl IntoView {
let h = RwSignal::new(40.0);
let s = RwSignal::new(40.0);
let l = RwSignal::new(40.0);
let red = RwSignal::new(0.2);
let green = RwSignal::new(0.2);
let blue = RwSignal::new(0.2);
let update_rgb = move |get_values: Box<dyn Fn() -> (f32, f32, f32)>| {
let (h_val, s_val, l_val) = get_values();
let rgb: Srgb = Hsl::new(h_val, s_val, l_val).into_color();
red.set(rgb.red);
green.set(rgb.green);
blue.set(rgb.blue);
};
(
(
create_color_sliders("Hue", h, move |new_h| {
h.set(new_h);
update_rgb(Box::new(move || {
(new_h * 3.6, s.get() / 100.0, l.get() / 100.0)
}))
}),
create_color_sliders("Saturation", s, move |new_s| {
s.set(new_s);
update_rgb(Box::new(move || {
(h.get() * 3.6, new_s / 100.0, l.get() / 100.0)
}))
}),
create_color_sliders("Lightness", l, move |new_l| {
l.set(new_l);
update_rgb(Box::new(move || {
(h.get() * 3.6, s.get() / 100.0, new_l / 100.0)
}))
}),
)
.style(|s| s.flex_col().gap(3)),
create_color_display(red, green, blue),
)
.style(|s| s.flex_col().items_start().margin_right(10))
}
fn hsv_view() -> impl IntoView {
let h = RwSignal::new(40.0);
let s = RwSignal::new(40.0);
let v = RwSignal::new(40.0);
let red = RwSignal::new(0.2);
let green = RwSignal::new(0.2);
let blue = RwSignal::new(0.2);
let update_rgb = move |get_values: Box<dyn Fn() -> (f32, f32, f32)>| {
let (h_val, s_val, v_val) = get_values();
let rgb: Srgb = Hsv::new(h_val, s_val, v_val).into_color();
red.set(rgb.red);
green.set(rgb.green);
blue.set(rgb.blue);
};
(
(
create_color_sliders("Hue", h, move |new_h| {
h.set(new_h);
update_rgb(Box::new(move || {
(new_h * 3.6, s.get() / 100.0, v.get() / 100.0)
}));
}),
create_color_sliders("Saturation", s, move |new_s| {
s.set(new_s);
update_rgb(Box::new(move || {
(h.get() * 3.6, new_s / 100.0, v.get() / 100.0)
}))
}),
create_color_sliders("Value", v, move |new_v| {
v.set(new_v);
update_rgb(Box::new(move || {
(h.get() * 3.6, s.get() / 100.0, new_v / 100.0)
}))
}),
)
.style(|s| s.flex_col().gap(3)),
create_color_display(red, green, blue),
)
.style(|s| s.flex_col().items_start().margin_right(10))
}
fn rgb_view() -> impl IntoView {
let r = RwSignal::new(40);
let g = RwSignal::new(40);
let b = RwSignal::new(40);
(
(
create_color_sliders("Red", RwSignal::new(r.get() as f32), move |new_r| {
r.set(new_r as i32)
}),
create_color_sliders("Green", RwSignal::new(g.get() as f32), move |new_g| {
g.set(new_g as i32)
}),
create_color_sliders("Blue", RwSignal::new(b.get() as f32), move |new_b| {
b.set(new_b as i32)
}),
)
.style(|s| s.flex_col().gap(3)),
(
dyn_view(move || format!("rgb: ({}, {}, {})", r.get(), g.get(), b.get(),))
.style(|s| s.width(50).font_size(18)),
empty().style(move |s| {
s.background(Color::rgb8(r.get() as u8, g.get() as u8, b.get() as u8))
.size(310, 75)
}),
)
.style(|s| s.flex_col().gap(10)),
)
.style(|s| s.flex_col().items_start().margin_right(10))
}
fn palette() -> impl IntoView {
(
(
(
(
empty().style(|s| {
s.background(Color::rgb8(194, 255, 199))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#C2FFC7".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(158, 223, 156))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#9EDF9C".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(98, 130, 93))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#62825D".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(82, 110, 72))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#526E48".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
(
(
empty().style(|s| {
s.background(Color::rgb8(223, 242, 235))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#DFF2EB".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(185, 229, 232))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#B9E5E8".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(122, 178, 211))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#7AB2D3".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(74, 98, 138))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#4A628A".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
(
(
empty().style(|s| {
s.background(Color::rgb8(116, 9, 56))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#740938".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(175, 23, 64))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#AF1740".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(204, 43, 82))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#CC2B52".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(222, 124, 125))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#DE7C7D".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
(
(
empty().style(|s| {
s.background(Color::rgb8(255, 245, 228))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FFF5E4".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(255, 227, 225))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FFE3E1".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(255, 209, 209))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FFD1D1".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(255, 148, 148))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FF9494".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
)
.style(|s| s.flex_col().gap(10)),
(
(
(
empty().style(|s| {
s.background(Color::rgb8(203, 157, 240))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#CB9DF0".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(240, 193, 225))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#F0C1E1".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(253, 219, 187))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FDDBBB".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(255, 249, 191))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FFF9BF".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
(
(
empty().style(|s| {
s.background(Color::rgb8(46, 7, 63))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#2E073F".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(122, 28, 172))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#7A1CAC".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(173, 73, 225))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#AD49E1".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(235, 211, 248))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#EBD3F8".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
(
(
empty().style(|s| {
s.background(Color::rgb8(111, 78, 55))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#6F4E37".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(166, 123, 91))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#A67B5B".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(236, 177, 118))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#ECB176".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(254, 216, 177))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FED8B1".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
(
(
empty().style(|s| {
s.background(Color::rgb8(204, 213, 174))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#CCD5AE".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(224, 229, 182))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#E0E5B6".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(250, 237, 206))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FAEDCE".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
(
empty().style(|s| {
s.background(Color::rgb8(254, 250, 224))
.size(235, 185)
.position(Position::Relative)
}),
label(move || "#FEFAE0".to_string())
.style(|s| s.position(Position::Absolute).padding(10).font_size(18)),
)
.style(|s| s.flex_col().items_center().gap(2)),
)
.style(|s| s.flex_row()),
)
.style(|s| s.flex_col().gap(10)),
)
.style(|s| s.flex_row().gap(20))
}
fn app_view() -> impl IntoView {
((rgb_view(), lch_view(), hsl_view(), hsv_view()), palette())
.style(|s| s.flex_col().padding(6).gap(10))
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
pub fn run() {
#[cfg(target_family = "wasm")]
console_error_panic_hook::set_once();
{
let mut font_system = FONT_SYSTEM.lock();
let font_db = font_system.db_mut();
font_db.load_font_data(Vec::from(FIRA_MONO));
font_db.load_font_data(Vec::from(FIRA_SANS));
font_db.load_font_data(Vec::from(DEJAVU_SERIF));
}
let window_config = WindowConfig::default().with_web_config(|w| w.canvas_id("the-canvas"));
Application::new()
.window(move |_| app_view(), Some(window_config))
.run()
}
fn main() {
run();
}
index.html
文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>floem plattle</title>
<style>
html,
body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background-color: rgb(255, 255, 255)
}
#the-canvas {
/* Margin around the canvas */
margin: 16px;
background-color: black;
/* Account for 16px margin on each side */
width: calc(100% - 32px);
height: calc(100% - 32px);
/* Maintains aspect ratio, scales to fill space */
object-fit: contain;
}
/* Remove focus outline (e.g. on Firefox) */
#the-canvas:focus-visible {
outline: none;
}
</style>
</head>
<body>
<canvas id="the-canvas"></canvas>
</body>
</html>