use web_sys::{HtmlElement, Window};
pub struct AnimationContext {
element: HtmlElement,
window: Window,
previous_time: f64,
current_time: f64,
delta_time: f64,
}
impl AnimationContext {
pub fn new(element: &HtmlElement) -> Self {
let window = web_sys::window().expect("No window reference");
let now = window.performance().map(|p| p.now()).unwrap_or(0.0);
Self {
element: element.clone(),
window,
previous_time: now,
current_time: now,
delta_time: 16.67, }
}
pub fn new_with_timing(element: &HtmlElement, previous_time: f64, current_time: f64) -> Self {
let window = web_sys::window().expect("No window reference");
let delta_time = current_time - previous_time;
Self {
element: element.clone(),
window,
previous_time,
current_time,
delta_time: delta_time.max(0.0),
}
}
pub fn update_timing(&mut self, new_time: f64) {
self.previous_time = self.current_time;
self.current_time = new_time;
self.delta_time = (self.current_time - self.previous_time).max(0.0);
}
pub fn element(&self) -> &HtmlElement {
&self.element
}
pub fn window(&self) -> &Window {
&self.window
}
pub fn element_width(&self) -> f64 {
self.element.offset_width() as f64
}
pub fn element_height(&self) -> f64 {
self.element.offset_height() as f64
}
pub fn client_width(&self) -> f64 {
self.element.client_width() as f64
}
pub fn client_height(&self) -> f64 {
self.element.client_height() as f64
}
pub fn scroll_width(&self) -> f64 {
self.element.scroll_width() as f64
}
pub fn scroll_height(&self) -> f64 {
self.element.scroll_height() as f64
}
pub fn bounding_rect(&self) -> web_sys::DomRect {
self.element.get_bounding_client_rect()
}
pub fn offset_left(&self) -> f64 {
self.element.offset_left() as f64
}
pub fn offset_top(&self) -> f64 {
self.element.offset_top() as f64
}
pub fn viewport_left(&self) -> f64 {
self.bounding_rect().left()
}
pub fn viewport_top(&self) -> f64 {
self.bounding_rect().top()
}
pub fn viewport_right(&self) -> f64 {
self.bounding_rect().right()
}
pub fn viewport_bottom(&self) -> f64 {
self.bounding_rect().bottom()
}
pub fn mouse_x_viewport(&self) -> f64 {
0.0
}
pub fn mouse_y_viewport(&self) -> f64 {
0.0
}
pub fn mouse_x(&self) -> f64 {
self.mouse_x_viewport() - self.viewport_left()
}
pub fn mouse_y(&self) -> f64 {
self.mouse_y_viewport() - self.viewport_top()
}
pub fn mouse_x_percent(&self) -> f64 {
let width = self.element_width();
if width > 0.0 {
(self.mouse_x() / width) * 100.0
} else {
0.0
}
}
pub fn mouse_y_percent(&self) -> f64 {
let height = self.element_height();
if height > 0.0 {
(self.mouse_y() / height) * 100.0
} else {
0.0
}
}
pub fn scroll_x(&self) -> f64 {
self.element.scroll_left() as f64
}
pub fn scroll_y(&self) -> f64 {
self.element.scroll_top() as f64
}
pub fn document_scroll_x(&self) -> f64 {
self.document()
.and_then(|doc| doc.body())
.map(|body| body.scroll_left() as f64)
.or_else(|| self.window.scroll_x().ok())
.unwrap_or(0.0)
}
pub fn document_scroll_y(&self) -> f64 {
self.document()
.and_then(|doc| doc.body())
.map(|body| body.scroll_top() as f64)
.or_else(|| self.window.scroll_y().ok())
.unwrap_or(0.0)
}
pub fn max_scroll_x(&self) -> f64 {
self.scroll_width() - self.client_width()
}
pub fn max_scroll_y(&self) -> f64 {
self.scroll_height() - self.client_height()
}
pub fn scroll_progress_x(&self) -> f64 {
let max = self.max_scroll_x();
if max > 0.0 {
self.scroll_x() / max
} else {
0.0
}
}
pub fn scroll_progress_y(&self) -> f64 {
let max = self.max_scroll_y();
if max > 0.0 {
self.scroll_y() / max
} else {
0.0
}
}
pub fn window_width(&self) -> f64 {
self.window
.inner_width()
.ok()
.and_then(|w| w.as_f64())
.unwrap_or(0.0)
}
pub fn window_height(&self) -> f64 {
self.window
.inner_height()
.ok()
.and_then(|h| h.as_f64())
.unwrap_or(0.0)
}
pub fn now(&self) -> f64 {
self.current_time
}
pub fn previous_time(&self) -> f64 {
self.previous_time
}
pub fn delta_time(&self) -> f64 {
self.delta_time
}
pub fn delta_seconds(&self) -> f64 {
self.delta_time / 1000.0
}
pub fn fps(&self) -> f64 {
if self.delta_time > 0.0 {
1000.0 / self.delta_time
} else {
60.0
}
}
fn document(&self) -> Option<web_sys::Document> {
self.window.document()
}
pub fn distance_from_center(&self) -> f64 {
let center_x = self.element_width() / 2.0;
let center_y = self.element_height() / 2.0;
let dx = self.mouse_x() - center_x;
let dy = self.mouse_y() - center_y;
(dx * dx + dy * dy).sqrt()
}
pub fn angle_from_center(&self) -> f64 {
let center_x = self.element_width() / 2.0;
let center_y = self.element_height() / 2.0;
(self.mouse_y() - center_y).atan2(self.mouse_x() - center_x)
}
pub fn is_mouse_inside(&self) -> f64 {
let x = self.mouse_x();
let y = self.mouse_y();
let width = self.element_width();
let height = self.element_height();
if x >= 0.0 && x <= width && y >= 0.0 && y <= height {
1.0
} else {
0.0
}
}
}