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 .overflow(Overflow::Clip)
128 .on_pointer_down(on_pointer_down)
129 .on_sized(move |e: Event<SizedEventData>| container_size.set(e.area.size))
130 .children(self.children.clone())
131 .children(ripples.read().iter().map(|ripple| {
132 RippleCircle {
133 id: ripple.id,
134 center: ripple.center,
135 color,
136 duration,
137 max_size,
138 ripples,
139 }
140 .into()
141 }))
142 }
143
144 fn render_key(&self) -> DiffKey {
145 self.key.clone().or(self.default_key())
146 }
147}
148
149#[derive(Clone, PartialEq)]
150struct RippleCircle {
151 id: u64,
152 center: Point2D,
153 color: Color,
154 duration: Duration,
155 max_size: f32,
156 ripples: State<Vec<RippleInstance>>,
157}
158
159impl Component for RippleCircle {
160 fn render(&self) -> impl IntoElement {
161 let id = self.id;
162 let mut ripples = self.ripples;
163
164 let animation = use_animation_with_dependencies(
165 &(self.max_size, self.duration),
166 move |conf, (max_size, duration)| {
167 conf.on_creation(OnCreation::Run);
168
169 (
170 AnimNum::new(0., *max_size)
171 .duration(*duration)
172 .function(Function::Expo)
173 .ease(Ease::Out),
174 AnimNum::new(0.35, 0.)
175 .duration(*duration)
176 .function(Function::Linear)
177 .ease(Ease::Out),
178 )
179 },
180 );
181
182 use_side_effect(move || {
183 if !*animation.is_running().read() && *animation.has_run_yet().read() {
184 ripples.write().retain(|r| r.id != id);
185 }
186 });
187
188 let (size, opacity) = animation.get().value();
189 let half = size / 2.0;
190
191 rect()
192 .interactive(false)
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}