Skip to content

Commit 09cdf50

Browse files
committed
Make graph view flickable
1 parent f89c844 commit 09cdf50

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

src/views/graph_view/controls.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,32 @@ use crate::views::pointer_handler::{MouseHandler, PointerEvent};
55
use crate::views::utils::{Coordinates, Delta};
66
use std::cell::RefCell;
77
use std::rc::{Rc, Weak};
8+
use std::time::Duration;
89
use wasm_bindgen::JsValue;
910
use web_sys::HtmlElement;
1011

12+
const REST_TIME_THRESHOLD: Duration = Duration::from_millis(10);
13+
1114
pub struct Controls {
1215
on_event_cb: Box<OnPointerEventCb>,
1316
// TODO(Menno 04.09.2025) Track multiple pointers for gestures
1417
drag_pointer_index: Option<i32>,
1518
previous_drag_coordinates: Coordinates,
19+
previous_drag_timestamp: Duration,
20+
drag_velocity: Delta,
1621
_pointer_handler: Rc<RefCell<MouseHandler>>,
1722
}
1823

1924
/// The callback type for the handler to call on a mouse event
2025
pub type OnPointerEventCb = dyn FnMut(ControlEvent);
2126

2227
pub enum ControlEvent {
28+
/// Contains the contact point on the canvas
2329
Down(Coordinates),
30+
/// Contains the delta in pixels since the last event
2431
Move(Delta),
25-
Up(),
32+
/// Contains the current drag velocity in pixels per seconds
33+
Up(Delta),
2634
}
2735

2836
impl Controls {
@@ -36,6 +44,8 @@ impl Controls {
3644
on_event_cb,
3745
drag_pointer_index: None,
3846
previous_drag_coordinates: Coordinates::zero(),
47+
previous_drag_timestamp: Duration::from_secs(0),
48+
drag_velocity: Delta::zero(),
3949
_pointer_handler: MouseHandler::new(
4050
target,
4151
Box::new(move |event| -> bool {
@@ -50,24 +60,37 @@ impl Controls {
5060
fn handle_event(&mut self, event: PointerEvent) -> bool {
5161
let mut handled = false;
5262
match event {
53-
PointerEvent::Down((index, _timestamp, coordinates)) => {
63+
PointerEvent::Down((index, timestamp, coordinates)) => {
5464
if self.drag_pointer_index.is_none() {
5565
self.drag_pointer_index = Some(index);
5666
self.previous_drag_coordinates = coordinates;
67+
self.previous_drag_timestamp = timestamp;
68+
self.drag_velocity = Delta::zero();
5769
(self.on_event_cb)(ControlEvent::Down(coordinates));
5870
handled = true;
5971
}
6072
}
61-
PointerEvent::Up((index, _timestamp, _coordinates)) => {
73+
PointerEvent::Up((index, timestamp, _coordinates)) => {
6274
if self.drag_pointer_index == Some(index) {
6375
self.drag_pointer_index = None;
64-
(self.on_event_cb)(ControlEvent::Up());
76+
77+
// If the last move event was a while ago, then we ignore the velocity.
78+
let time_delta = timestamp - self.previous_drag_timestamp;
79+
if time_delta > REST_TIME_THRESHOLD {
80+
self.drag_velocity = Delta::zero();
81+
}
82+
83+
(self.on_event_cb)(ControlEvent::Up(self.drag_velocity));
6584
handled = true;
6685
}
6786
}
68-
PointerEvent::Move((index, _timestamp, coordinates)) => {
87+
PointerEvent::Move((index, timestamp, coordinates)) => {
6988
if self.drag_pointer_index == Some(index) {
7089
let delta = coordinates - self.previous_drag_coordinates;
90+
let delta_time = timestamp - self.previous_drag_timestamp;
91+
self.drag_velocity = delta / delta_time.as_secs_f64();
92+
self.previous_drag_timestamp = timestamp;
93+
7194
(self.on_event_cb)(ControlEvent::Move(delta));
7295
self.previous_drag_coordinates = coordinates;
7396
handled = true;

src/views/graph_view/mod.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::views::graph_view::controls::{ControlEvent, Controls};
99
use crate::views::graph_view::renderer::Renderer;
1010
use crate::views::resize_observer::ResizeObserver;
1111
use crate::views::utils::get_element_of_type;
12+
use euclid::approxeq::ApproxEq;
1213
use euclid::{Scale, Size2D, Transform2D, Vector2D};
1314
use std::cell::RefCell;
1415
use std::rc::{Rc, Weak};
@@ -62,6 +63,12 @@ impl ClipSpace {
6263
}
6364
}
6465

66+
/// The decay factor of the drag velocity, per second
67+
const TRANSLATION_DRAG_FACTOR: f32 = 0.5;
68+
69+
/// Zero velocity
70+
const TRANSLATION_VELOCITY_ZERO: Vector2D<f32, ClipSpace> = Vector2D::new(0.0, 0.0);
71+
6572
/// This represents the Canvas coordinate system, where the canvas is represented in [0, pixel size]
6673
struct CanvasSpace;
6774

@@ -86,6 +93,7 @@ pub struct GraphView {
8693
canvas_to_clip: Transform2D<f32, CanvasSpace, ClipSpace>,
8794
zoom: Scale<f32, ClipSpace, ClipSpace>,
8895
translation: Vector2D<f32, ClipSpace>,
96+
translation_velocity: Vector2D<f32, ClipSpace>,
8997
view_transform: [f32; 9],
9098
renderer: Renderer,
9199
}
@@ -137,6 +145,7 @@ impl GraphView {
137145
canvas_to_clip: Transform2D::identity(),
138146
zoom: Scale::identity(),
139147
translation: ClipSpace::CLIP_SPACE_OFFSET,
148+
translation_velocity: Vector2D::zero(),
140149
view_transform: [0.0; 9],
141150
renderer,
142151
})
@@ -159,14 +168,28 @@ impl GraphView {
159168
self.frame_scheduler.schedule().unwrap();
160169
}
161170

162-
fn draw(&mut self, _timestamp: Duration) {
171+
fn draw(&mut self, timestamp: Duration) {
172+
// Update translation in case of flick
173+
self.translation += self.translation_velocity * timestamp.as_secs_f32();
174+
// self.translation_velocity *= TRANSLATION_DRAG_FACTOR * timestamp.as_secs_f32();
175+
176+
// Resize canvas if needed
163177
if self.canvas_needs_size_update {
164178
self.canvas_needs_size_update = false;
165179
self.canvas.set_width(self.canvas_size.width as u32);
166180
self.canvas.set_height(self.canvas_size.height as u32);
167181
}
168182

169-
self.renderer.draw(&self.view_transform)
183+
// Draw
184+
self.renderer.draw(&self.view_transform);
185+
186+
// Schedule next draw if needed
187+
if !self
188+
.translation_velocity
189+
.approx_eq(&TRANSLATION_VELOCITY_ZERO)
190+
{
191+
self.schedule_draw();
192+
}
170193
}
171194

172195
pub fn set_data(&mut self, graph: &Graph, active_state: BoardId) {
@@ -188,11 +211,20 @@ impl GraphView {
188211

189212
fn handle_pointer_event(&mut self, event: ControlEvent) {
190213
match event {
191-
ControlEvent::Down(_coordinates) => {}
192-
ControlEvent::Move(coordinates) => {
193-
self.handle_translation(Vector2D::new(coordinates.x as f32, -coordinates.y as f32))
214+
ControlEvent::Down(_coordinates) => {
215+
// Cancel a flick if it was still going
216+
self.translation_velocity = Vector2D::zero();
217+
}
218+
ControlEvent::Move(delta_coordinates) => self.handle_translation(Vector2D::new(
219+
delta_coordinates.x as f32,
220+
-delta_coordinates.y as f32,
221+
)),
222+
ControlEvent::Up(velocity) => {
223+
// Store the current drag velocity so that the chart can be flicked
224+
self.translation_velocity = self
225+
.canvas_to_clip
226+
.transform_vector(Vector2D::new(velocity.x as f32, -velocity.y as f32));
194227
}
195-
ControlEvent::Up() => {}
196228
}
197229
}
198230

0 commit comments

Comments
 (0)