Skip to main content

euv_ui/component/touch/hook/
impl.rs

1use super::*;
2
3/// Implementation of touch point extraction from DOM touch events.
4impl NativeTouchPoint {
5    /// Extracts all active touch points from a `TouchEvent`.
6    ///
7    /// Iterates over the `touches` list of the given `TouchEvent` and
8    /// builds a `Vec<NativeTouchPoint>` with each touch point's
9    /// identifier, viewport coordinates, screen coordinates, page
10    /// coordinates, and offset coordinates relative to the target element.
11    ///
12    /// The offset coordinates (`offset_x`, `offset_y`) are computed by
13    /// subtracting the target element's bounding rect from the touch's
14    /// client coordinates, since the browser `Touch` object does not
15    /// provide `offsetX`/`offsetY` directly.
16    ///
17    /// Uses web-sys typed getters (`TouchEvent::touches()`,
18    /// `TouchList::get`, `Touch::client_x()`) instead of
19    /// `Reflect::get(event, "clientX")`. The Reflect path allocates a
20    /// `JsValue::from_str` per field per touch (7 fields × N touches per
21    /// event) on every `touchmove` (60-120Hz); the typed getters skip
22    /// the string lookup and the per-field JS string allocation.
23    ///
24    /// # Arguments
25    ///
26    /// - `&Event` - The native DOM touch event.
27    ///
28    /// # Returns
29    ///
30    /// - `Vec<NativeTouchPoint>` - All currently active touch points.
31    pub fn extract_all(event: &Event) -> Vec<NativeTouchPoint> {
32        let touch_event: &TouchEvent = event.unchecked_ref::<TouchEvent>();
33        let touches: TouchList = touch_event.touches();
34        let target: JsValue = event
35            .target()
36            .map_or(JsValue::NULL, |event_target: EventTarget| {
37                event_target.into()
38            });
39        let element: Element = target.unchecked_into();
40        let rect: DomRect = element.get_bounding_client_rect();
41        let rect_left: f64 = rect.left();
42        let rect_top: f64 = rect.top();
43        let length: u32 = touches.length();
44        (0..length)
45            .filter_map(|index: u32| touches.get(index))
46            .map(|touch: Touch| {
47                let identifier: i32 = touch.identifier();
48                let client_x: i32 = touch.client_x();
49                let client_y: i32 = touch.client_y();
50                let screen_x: i32 = touch.screen_x();
51                let screen_y: i32 = touch.screen_y();
52                let page_x: i32 = touch.page_x();
53                let page_y: i32 = touch.page_y();
54                let offset_x: i32 = (client_x as f64 - rect_left).round() as i32;
55                let offset_y: i32 = (client_y as f64 - rect_top).round() as i32;
56                NativeTouchPoint {
57                    identifier,
58                    client_x,
59                    client_y,
60                    screen_x,
61                    screen_y,
62                    offset_x,
63                    offset_y,
64                    page_x,
65                    page_y,
66                }
67            })
68            .collect()
69    }
70
71    /// Extracts the changed touch points from a `TouchEvent`.
72    ///
73    /// The `changedTouches` list contains touch points that have changed
74    /// since the last touch event:
75    /// - For `touchstart` - newly added touch points.
76    /// - For `touchmove` - touch points that have moved.
77    /// - For `touchend` / `touchcancel` - removed touch points.
78    ///
79    /// This is useful for determining which specific fingers were lifted
80    /// in a `touchend` event, since the `touches` list no longer contains
81    /// them.
82    ///
83    /// Uses web-sys typed getters (`TouchEvent::changed_touches()`,
84    /// `TouchList::get`, `Touch::client_x()`) to avoid the per-field
85    /// `Reflect::get` + `JsValue::from_str` allocation cost on the hot
86    /// `touchmove` path.
87    ///
88    /// # Arguments
89    ///
90    /// - `&Event` - The native DOM touch event.
91    ///
92    /// # Returns
93    ///
94    /// - `Vec<NativeTouchPoint>` - The touch points that changed in this event.
95    pub fn extract_changed(event: &Event) -> Vec<NativeTouchPoint> {
96        let touch_event: &TouchEvent = event.unchecked_ref::<TouchEvent>();
97        let touches: TouchList = touch_event.changed_touches();
98        let target: JsValue = event
99            .target()
100            .map_or(JsValue::NULL, |event_target: EventTarget| {
101                event_target.into()
102            });
103        let element: Element = target.unchecked_into();
104        let rect: DomRect = element.get_bounding_client_rect();
105        let rect_left: f64 = rect.left();
106        let rect_top: f64 = rect.top();
107        let length: u32 = touches.length();
108        (0..length)
109            .filter_map(|index: u32| touches.get(index))
110            .map(|touch: Touch| {
111                let identifier: i32 = touch.identifier();
112                let client_x: i32 = touch.client_x();
113                let client_y: i32 = touch.client_y();
114                let screen_x: i32 = touch.screen_x();
115                let screen_y: i32 = touch.screen_y();
116                let page_x: i32 = touch.page_x();
117                let page_y: i32 = touch.page_y();
118                let offset_x: i32 = (client_x as f64 - rect_left).round() as i32;
119                let offset_y: i32 = (client_y as f64 - rect_top).round() as i32;
120                NativeTouchPoint {
121                    identifier,
122                    client_x,
123                    client_y,
124                    screen_x,
125                    screen_y,
126                    offset_x,
127                    offset_y,
128                    page_x,
129                    page_y,
130                }
131            })
132            .collect()
133    }
134}
135
136/// Implementation of high-precision touch point extraction from DOM touch events.
137impl NativeTouchPointF64 {
138    /// Extracts all active touch points with high-precision `f64` offset coordinates
139    /// from a `TouchEvent`.
140    ///
141    /// Similar to `NativeTouchPoint::extract_all`, but returns `f64` precision for
142    /// offset/client coordinates, which is essential for canvas drawing
143    /// and other pixel-precise interactions.
144    ///
145    /// Uses web-sys typed getters (`TouchEvent::touches()`,
146    /// `TouchList::get`, `Touch::client_x()`) instead of
147    /// `Reflect::get(event, "clientX")`. web-sys `Touch` exposes
148    /// `client_x`/`page_x`/etc. as `i32`; we widen to `f64` to preserve
149    /// the `NativeTouchPointF64` high-precision contract without losing
150    /// sub-pixel information on the offset computation.
151    ///
152    /// # Arguments
153    ///
154    /// - `&Event` - The native DOM touch event.
155    ///
156    /// # Returns
157    ///
158    /// - `Vec<NativeTouchPointF64>` - All currently active touch points with `f64` coordinates.
159    pub fn extract_all(event: &Event) -> Vec<NativeTouchPointF64> {
160        let touch_event: &TouchEvent = event.unchecked_ref::<TouchEvent>();
161        let touches: TouchList = touch_event.touches();
162        let target: JsValue = event
163            .target()
164            .map_or(JsValue::NULL, |event_target: EventTarget| {
165                event_target.into()
166            });
167        let element: Element = target.unchecked_into();
168        let rect: DomRect = element.get_bounding_client_rect();
169        let rect_left: f64 = rect.left();
170        let rect_top: f64 = rect.top();
171        let length: u32 = touches.length();
172        (0..length)
173            .filter_map(|index: u32| touches.get(index))
174            .map(|touch: Touch| {
175                let identifier: i32 = touch.identifier();
176                let client_x: f64 = touch.client_x() as f64;
177                let client_y: f64 = touch.client_y() as f64;
178                let screen_x: f64 = touch.screen_x() as f64;
179                let screen_y: f64 = touch.screen_y() as f64;
180                let page_x: f64 = touch.page_x() as f64;
181                let page_y: f64 = touch.page_y() as f64;
182                let offset_x: f64 = client_x - rect_left;
183                let offset_y: f64 = client_y - rect_top;
184                NativeTouchPointF64 {
185                    identifier,
186                    client_x,
187                    client_y,
188                    screen_x,
189                    screen_y,
190                    offset_x,
191                    offset_y,
192                    page_x,
193                    page_y,
194                }
195            })
196            .collect()
197    }
198}