yew_hooks/hooks/
use_swipe.rs1use std::cmp::max;
2use std::rc::Rc;
3
4use yew::prelude::*;
5
6use super::{use_event, use_mut_latest};
7
8#[derive(PartialEq, Eq, Clone, Debug)]
10pub enum UseSwipeDirection {
11 Up,
12 Right,
13 Down,
14 Left,
15 None,
16}
17
18#[derive(Default)]
20pub struct UseSwipeOptions {
21 pub threshold: Option<u32>,
22 pub onswipestart: Option<Box<dyn FnMut(TouchEvent)>>,
23 pub onswipe: Option<Box<dyn FnMut(TouchEvent)>>,
24 pub onswipeend: Option<Box<dyn FnMut(TouchEvent, UseSwipeDirection)>>,
25}
26
27pub struct UseSwipeHandle {
29 pub swiping: UseStateHandle<bool>,
30 pub direction: UseStateHandle<UseSwipeDirection>,
31 pub coords_start: UseStateHandle<(i32, i32)>,
32 pub coords_end: UseStateHandle<(i32, i32)>,
33 pub length_x: UseStateHandle<i32>,
34 pub length_y: UseStateHandle<i32>,
35}
36
37impl Clone for UseSwipeHandle {
38 fn clone(&self) -> Self {
39 Self {
40 swiping: self.swiping.clone(),
41 direction: self.direction.clone(),
42 coords_start: self.coords_start.clone(),
43 coords_end: self.coords_end.clone(),
44 length_x: self.length_x.clone(),
45 length_y: self.length_y.clone(),
46 }
47 }
48}
49
50#[hook]
99pub fn use_swipe(node: NodeRef) -> UseSwipeHandle {
100 use_swipe_with_options(node, UseSwipeOptions::default())
101}
102
103#[hook]
106pub fn use_swipe_with_window() -> UseSwipeHandle {
107 use_swipe_with_options(NodeRef::default(), UseSwipeOptions::default())
108}
109
110#[hook]
150pub fn use_swipe_with_options(node: NodeRef, options: UseSwipeOptions) -> UseSwipeHandle {
151 let swiping = use_state_eq(|| false);
152 let direction = use_state_eq(|| UseSwipeDirection::None);
153 let coords_start = use_state(|| (0, 0));
154 let coords_end = use_state(|| (0, 0));
155 let length_x = use_state(|| 0);
156 let length_y = use_state(|| 0);
157
158 let threshold = options.threshold.unwrap_or(50);
159 let onswipestart = use_mut_latest(options.onswipestart);
160 let onswipe = use_mut_latest(options.onswipe);
161 let onswipeend = use_mut_latest(options.onswipeend);
162
163 let diff_x = {
164 let coords_start = coords_start.clone();
165 let coords_end = coords_end.clone();
166 #[allow(clippy::unnecessary_cast)]
167 Rc::new(move || (coords_start.0 - coords_end.0) as i32)
168 };
169
170 let diff_y = {
171 let coords_start = coords_start.clone();
172 let coords_end = coords_end.clone();
173 #[allow(clippy::unnecessary_cast)]
174 Rc::new(move || (coords_start.1 - coords_end.1) as i32)
175 };
176
177 let ontouchend = {
178 let swiping = swiping.clone();
179 let direction = direction.clone();
180 Rc::new(move |e: TouchEvent| {
181 if *swiping {
182 let onswipeend = onswipeend.current();
183 let onswipeend = &mut *onswipeend.borrow_mut();
184 if let Some(onswipeend) = onswipeend {
185 onswipeend(e, (*direction).clone());
186 }
187 }
188
189 swiping.set(false);
190 direction.set(UseSwipeDirection::None);
191 })
192 };
193
194 let threshold_exceeded = {
195 let diff_x = diff_x.clone();
196 let diff_y = diff_y.clone();
197 Rc::new(move || max(diff_x().abs(), diff_y().abs()) >= (threshold as i32))
198 };
199
200 {
201 let node = node.clone();
202 let coords_start = coords_start.clone();
203 let coords_end = coords_end.clone();
204 use_event(node, "touchstart", move |e: TouchEvent| {
205 let x = e.touches().get(0).map_or(0, |t| t.client_x());
206 let y = e.touches().get(0).map_or(0, |t| t.client_y());
207 coords_start.set((x, y));
208 coords_end.set((x, y));
209
210 let onswipestart = onswipestart.current();
211 let onswipestart = &mut *onswipestart.borrow_mut();
212 if let Some(onswipestart) = onswipestart {
213 onswipestart(e);
214 }
215 });
216 }
217
218 {
219 let node = node.clone();
220 let coords_end = coords_end.clone();
221 let swiping = swiping.clone();
222 let length_x = length_x.clone();
223 let length_y = length_y.clone();
224 let direction = direction.clone();
225 use_event(node, "touchmove", move |e: TouchEvent| {
226 let x = e.touches().get(0).map_or(0, |t| t.client_x());
227 let y = e.touches().get(0).map_or(0, |t| t.client_y());
228 coords_end.set((x, y));
229 length_x.set(diff_x());
230 length_y.set(diff_y());
231
232 if !*swiping && threshold_exceeded() {
233 swiping.set(true);
234 }
235
236 if !threshold_exceeded() {
237 direction.set(UseSwipeDirection::None);
238 } else if diff_x().abs() > diff_y().abs() {
239 if diff_x() > 0 {
240 direction.set(UseSwipeDirection::Left);
241 } else {
242 direction.set(UseSwipeDirection::Right);
243 }
244 } else if diff_y() > 0 {
245 direction.set(UseSwipeDirection::Up);
246 } else {
247 direction.set(UseSwipeDirection::Down);
248 }
249
250 if *swiping {
251 let onswipe = onswipe.current();
252 let onswipe = &mut *onswipe.borrow_mut();
253 if let Some(onswipe) = onswipe {
254 onswipe(e);
255 }
256 }
257 });
258 }
259
260 {
261 let node = node.clone();
262 let ontouchend = ontouchend.clone();
263 use_event(node, "touchend", move |e: TouchEvent| {
264 ontouchend(e);
265 });
266 }
267
268 {
269 let ontouchend = ontouchend.clone();
270 use_event(node, "touchcancel", move |e: TouchEvent| {
271 ontouchend(e);
272 });
273 }
274
275 UseSwipeHandle {
276 swiping,
277 direction,
278 coords_start,
279 coords_end,
280 length_x,
281 length_y,
282 }
283}