lepticons_animate/
draw_icon.rs1use leptos::prelude::*;
2use leptos::text_prop::TextProp;
3use lepticons::{Glyph, LucideGlyph, DEFAULT_SIZE, DEFAULT_FILL, DEFAULT_STROKE, DEFAULT_STROKE_WIDTH};
4use wasm_bindgen::JsCast;
5
6use crate::Easing;
7
8#[component]
25pub fn DrawIcon(
26 #[prop(into)]
28 glyph: Signal<LucideGlyph>,
29 #[prop(default = 600)]
31 duration_ms: u32,
32 #[prop(default = 0)]
34 delay_ms: u32,
35 #[prop(default = Easing::EaseInOut)]
37 easing: Easing,
38 #[prop(into, optional)]
40 class: Option<TextProp>,
41 #[prop(into, optional)]
43 size: Option<TextProp>,
44 #[prop(into, optional)]
46 fill: Option<TextProp>,
47 #[prop(into, optional)]
49 stroke: Option<TextProp>,
50 #[prop(into, optional)]
52 stroke_width: Option<TextProp>,
53) -> impl IntoView {
54 let size = size.unwrap_or_else(|| DEFAULT_SIZE.into());
55 let size2 = size.clone();
56 let fill = fill.unwrap_or_else(|| DEFAULT_FILL.into());
57 let stroke = stroke.unwrap_or_else(|| DEFAULT_STROKE.into());
58 let stroke_width = stroke_width.unwrap_or_else(|| DEFAULT_STROKE_WIDTH.into());
59 let easing_css = easing.as_css();
60
61 let wrapper_ref = NodeRef::<leptos::html::Div>::new();
62
63 Effect::new(move |_| {
66 glyph.get();
67
68 let Some(wrapper) = wrapper_ref.get() else { return };
69 let wrapper_el: web_sys::HtmlElement = (*wrapper).clone();
70
71 request_animation_frame(move || {
73 let wrapper_as_el: &web_sys::Element = wrapper_el.as_ref();
74 let Some(svg) = wrapper_as_el.first_element_child() else { return };
75 let children = svg.children();
76 let count = children.length();
77
78 for i in 0..count {
79 let Some(child) = children.item(i) else { continue };
80
81 let Ok(geom) = child.clone().dyn_into::<web_sys::SvgGeometryElement>() else {
82 continue;
83 };
84 let length: f32 = geom.get_total_length();
85 if length <= 0.0 {
86 continue;
87 }
88
89 let svg_child: web_sys::SvgElement = child.unchecked_into();
90 let s = svg_child.style();
91
92 let len_str = length.to_string();
93 let _ = s.set_property("stroke-dasharray", &len_str);
95 let _ = s.set_property("stroke-dashoffset", &len_str);
96 let _ = s.set_property("transition", "none");
97
98 let s_clone = s.clone();
100 request_animation_frame(move || {
101 let _ = s_clone.set_property(
102 "transition",
103 &format!(
104 "stroke-dashoffset {}ms {} {}ms",
105 duration_ms, easing_css, delay_ms
106 ),
107 );
108 let _ = s_clone.set_property("stroke-dashoffset", "0");
109 });
110 }
111 });
112 });
113
114 view! {
115 <div node_ref=wrapper_ref
116 class=move || class.as_ref().map(|c| c.get().to_string()).unwrap_or_default()
117 style="display:inline-block;line-height:0">
118 <svg
119 xmlns="http://www.w3.org/2000/svg"
120 width=move || size.get()
121 height=move || size2.get()
122 viewBox="0 0 24 24"
123 fill=move || fill.get()
124 stroke=move || stroke.get()
125 stroke-width=move || stroke_width.get()
126 stroke-linecap="round"
127 stroke-linejoin="round"
128 inner_html=move || glyph.get().svg()
129 />
130 </div>
131 }
132}