freya_material_design/
ripple.rs1use std::time::Duration;
2
3use freya_animation::prelude::*;
4use freya_components::theming::hooks::get_theme_or_default;
5use freya_core::prelude::*;
6use torin::prelude::{
7 Point2D,
8 Position,
9 Size,
10 Size2D,
11};
12
13#[derive(Clone, PartialEq)]
15struct RippleInstance {
16 id: u64,
17 center: Point2D,
18}
19
20#[derive(Clone, PartialEq)]
38pub struct Ripple {
39 children: Vec<Element>,
40 layout: LayoutData,
41 key: DiffKey,
42 color: Option<Color>,
43 duration: Duration,
44}
45
46impl Default for Ripple {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl ChildrenExt for Ripple {
53 fn get_children(&mut self) -> &mut Vec<Element> {
54 &mut self.children
55 }
56}
57
58impl KeyExt for Ripple {
59 fn write_key(&mut self) -> &mut DiffKey {
60 &mut self.key
61 }
62}
63
64impl LayoutExt for Ripple {
65 fn get_layout(&mut self) -> &mut LayoutData {
66 &mut self.layout
67 }
68}
69
70impl ContainerExt for Ripple {}
71
72impl ContainerWithContentExt for Ripple {}
73
74impl Ripple {
75 pub fn new() -> Self {
76 Self {
77 children: Vec::new(),
78 layout: LayoutData::default(),
79 key: DiffKey::None,
80 color: None,
81 duration: Duration::from_millis(800),
82 }
83 }
84
85 pub fn color(mut self, color: impl Into<Color>) -> Self {
88 self.color = Some(color.into());
89 self
90 }
91
92 pub fn duration(mut self, duration: Duration) -> Self {
95 self.duration = duration;
96 self
97 }
98}
99
100impl Component for Ripple {
101 fn render(&self) -> impl IntoElement {
102 let mut container_size = use_state(Size2D::zero);
103 let mut ripples = use_state::<Vec<RippleInstance>>(Vec::new);
104 let mut ripple_counter = use_state(|| 0u64);
105
106 let color = self.color.unwrap_or_else(|| {
107 let theme = get_theme_or_default();
108 theme.read().colors.primary
109 });
110 let duration = self.duration;
111
112 let on_pointer_down = move |e: Event<PointerEventData>| {
113 let id = ripple_counter();
114 *ripple_counter.write() += 1;
115
116 ripples.write().push(RippleInstance {
117 id,
118 center: e.element_location().cast(),
119 });
120 };
121
122 let size = container_size();
123 let max_size = size.width.max(size.height) * 2.5;
124
125 rect()
126 .layout(self.layout.clone())
127 .interactive(false)
128 .overflow(Overflow::Clip)
129 .on_pointer_down(on_pointer_down)
130 .on_sized(move |e: Event<SizedEventData>| container_size.set(e.area.size))
131 .children(self.children.clone())
132 .children(ripples.read().iter().map(|ripple| {
133 RippleCircle {
134 id: ripple.id,
135 center: ripple.center,
136 color,
137 duration,
138 max_size,
139 ripples,
140 }
141 .into()
142 }))
143 }
144
145 fn render_key(&self) -> DiffKey {
146 self.key.clone().or(self.default_key())
147 }
148}
149
150#[derive(Clone, PartialEq)]
151struct RippleCircle {
152 id: u64,
153 center: Point2D,
154 color: Color,
155 duration: Duration,
156 max_size: f32,
157 ripples: State<Vec<RippleInstance>>,
158}
159
160impl Component for RippleCircle {
161 fn render(&self) -> impl IntoElement {
162 let id = self.id;
163 let mut ripples = self.ripples;
164
165 let animation = use_animation_with_dependencies(
166 &(self.max_size, self.duration),
167 move |conf, (max_size, duration)| {
168 conf.on_creation(OnCreation::Run);
169
170 (
171 AnimNum::new(0., *max_size)
172 .duration(*duration)
173 .function(Function::Expo)
174 .ease(Ease::Out),
175 AnimNum::new(0.35, 0.)
176 .duration(*duration)
177 .function(Function::Linear)
178 .ease(Ease::Out),
179 )
180 },
181 );
182
183 use_side_effect(move || {
184 if !*animation.is_running().read() && *animation.has_run_yet().read() {
185 ripples.write().retain(|r| r.id != id);
186 }
187 });
188
189 let (size, opacity) = animation.get().value();
190 let half = size / 2.0;
191
192 rect()
193 .position(
194 Position::new_absolute()
195 .left(self.center.x - half)
196 .top(self.center.y - half),
197 )
198 .width(Size::px(size))
199 .height(Size::px(size))
200 .corner_radius(CornerRadius::new_all(half))
201 .layer(1)
202 .background(self.color.with_a((opacity * 255.0) as u8))
203 }
204
205 fn render_key(&self) -> DiffKey {
206 DiffKey::U64(self.id)
207 }
208}