use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::{Event, HtmlElement};
use crate::spring::{Spring, SpringConfig};
use crate::traits::Update;
pub struct ScrollSmoother {
spring: Spring,
content_element: HtmlElement,
scroll_closure: Option<Closure<dyn FnMut(Event)>>,
attached: bool,
target_y: f32,
}
impl core::fmt::Debug for ScrollSmoother {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ScrollSmoother")
.field("attached", &self.attached)
.field("position", &self.spring.position())
.field("target_y", &self.target_y)
.finish()
}
}
impl ScrollSmoother {
pub fn new(content_element: HtmlElement, config: SpringConfig) -> Self {
Self {
spring: Spring::new(config),
content_element,
scroll_closure: None,
attached: false,
target_y: 0.0,
}
}
pub fn attach(&mut self) {
if self.attached {
return;
}
let _ = self
.content_element
.style()
.set_property("will-change", "transform");
let target = std::rc::Rc::new(std::cell::Cell::new(0.0_f32));
let target_clone = target.clone();
let closure = Closure::wrap(Box::new(move |_e: Event| {
if let Some(win) = web_sys::window() {
let scroll_y = win.scroll_y().unwrap_or(0.0) as f32;
target_clone.set(scroll_y);
}
}) as Box<dyn FnMut(Event)>);
if let Some(win) = web_sys::window() {
let _ =
win.add_event_listener_with_callback("scroll", closure.as_ref().unchecked_ref());
}
self.scroll_closure = Some(closure);
self.attached = true;
}
pub fn detach(&mut self) {
if !self.attached {
return;
}
if let Some(closure) = self.scroll_closure.take() {
if let Some(win) = web_sys::window() {
let _ = win.remove_event_listener_with_callback(
"scroll",
closure.as_ref().unchecked_ref(),
);
}
}
let _ = self.content_element.style().remove_property("will-change");
let _ = self.content_element.style().remove_property("transform");
self.attached = false;
}
pub fn tick(&mut self, dt: f32) {
if !self.attached {
return;
}
if let Some(win) = web_sys::window() {
self.target_y = win.scroll_y().unwrap_or(0.0) as f32;
}
self.spring.set_target(self.target_y);
self.spring.update(dt);
let y = self.spring.position();
let _ = self
.content_element
.style()
.set_property("transform", &format!("translateY(-{y}px)"));
}
pub fn position(&self) -> f32 {
self.spring.position()
}
pub fn target(&self) -> f32 {
self.target_y
}
pub fn spring_config_mut(&mut self) -> &mut SpringConfig {
&mut self.spring.config
}
}