use web_sys::PointerEvent;
const NUM_POINTERS: usize = 2;
pub struct PointerTracker {
slots: [Option<PointerEvent>; NUM_POINTERS],
}
pub enum PointerGesture {
Drag {
dx: i32,
dy: i32,
},
Pinch {
center: (i32, i32),
dilation: (f32, f32),
},
}
impl PointerTracker {
pub fn new() -> PointerTracker {
PointerTracker {
slots: Default::default(),
}
}
pub fn on_pointer_down(&mut self, event: PointerEvent) {
self.record_event(event);
}
#[allow(clippy::needless_return)]
fn record_event(&mut self, event: PointerEvent) {
let pointer_id = event.pointer_id();
if let Some(slot) = self.slots.iter_mut().find_map(|x| {
x.as_mut().and_then(|x| {
if x.pointer_id() == pointer_id {
Some(x)
} else {
None
}
})
}) {
*slot = event;
return;
}
if let Some(slot) = self.slots.iter_mut().find(|x| x.is_none()) {
slot.replace(event);
return;
}
}
#[allow(clippy::needless_return)]
pub fn on_pointer_up(&mut self, event: PointerEvent) {
let pointer_id = event.pointer_id();
if let Some(slot) = self
.slots
.iter_mut()
.find(|x| x.as_ref().map_or(false, |x| x.pointer_id() == pointer_id))
{
slot.take();
return;
}
}
fn get_event(&self, pointer_id: i32) -> Option<&PointerEvent> {
self.slots.iter().find_map(|x| {
x.as_ref().and_then(|x| {
if x.pointer_id() == pointer_id {
Some(x)
} else {
None
}
})
})
}
pub fn on_pointer_move(&mut self, event: PointerEvent) -> Option<PointerGesture> {
let ret = match self.num_active_pointers() {
1 => self
.get_event(event.pointer_id())
.map(|old_event| self.drag(&event, old_event)),
2 => self.pinch(&event),
_ => None,
};
if ret.is_some() {
self.record_event(event);
}
ret
}
pub fn has_active_pointers(&self) -> bool {
self.slots.iter().any(|x| x.is_some())
}
fn num_active_pointers(&self) -> usize {
self.slots.iter().filter(|x| x.is_some()).count()
}
fn drag(&self, new: &PointerEvent, old: &PointerEvent) -> PointerGesture {
PointerGesture::Drag {
dx: new.client_x() - old.client_x(),
dy: new.client_y() - old.client_y(),
}
}
fn pinch(&self, event: &PointerEvent) -> Option<PointerGesture> {
let pointer_id = event.pointer_id();
let same = self.get_event(pointer_id)?;
let other = self
.slots
.iter()
.find_map(|x| {
x.as_ref().and_then(|x| {
if x.pointer_id() != pointer_id {
Some(x)
} else {
None
}
})
})
.unwrap();
let same_x = same.client_x() as f32;
let same_y = same.client_y() as f32;
let other_x = other.client_x() as f32;
let other_y = other.client_y() as f32;
let new_x = event.client_x() as f32;
let new_y = event.client_y() as f32;
let min_dilation = 0.5;
let max_dilation = 2.0;
let min_distance = 10.0;
let dist_x = (same_x - other_x).abs();
let dist_y = (same_y - other_y).abs();
let dilation_x = if dist_x >= min_distance {
((new_x - other_x) / (same_x - other_x))
.abs()
.clamp(min_dilation, max_dilation)
} else {
1.0
};
let dilation_y = if dist_y >= min_distance {
((new_y - other_y) / (same_y - other_y))
.abs()
.clamp(min_dilation, max_dilation)
} else {
1.0
};
Some(PointerGesture::Pinch {
center: (other.client_x(), other.client_y()),
dilation: (dilation_x, dilation_y),
})
}
}
impl Default for PointerTracker {
fn default() -> PointerTracker {
PointerTracker::new()
}
}