Skip to content

Commit

Permalink
On Web, use requestAnimationFrame for RedrawRequested
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda authored and kchibisov committed Jun 23, 2023
1 parent e22079a commit 8558d09
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ And please only add new entries to the top of this list, right below the `# Unre

- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them.
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,9 @@ impl<T> EventLoopWindowTarget<T> {
}
},
);

let runner = self.runner.clone();
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
}

pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
Expand Down
62 changes: 62 additions & 0 deletions src/platform_impl/web/web_sys/animation_frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::cell::Cell;
use std::rc::Rc;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;

pub struct AnimationFrameHandler {
window: web_sys::Window,
closure: Closure<dyn FnMut()>,
handle: Rc<Cell<Option<i32>>>,
}

impl AnimationFrameHandler {
pub fn new(window: web_sys::Window) -> Self {
let handle = Rc::new(Cell::new(None));
let closure = Closure::new({
let handle = handle.clone();
move || handle.set(None)
});

Self {
window,
closure,
handle,
}
}

pub fn on_animation_frame<F>(&mut self, mut f: F)
where
F: 'static + FnMut(),
{
let handle = self.handle.clone();
self.closure = Closure::new(move || {
handle.set(None);
f();
})
}

pub fn request(&self) {
if let Some(handle) = self.handle.take() {
self.window
.cancel_animation_frame(handle)
.expect("Failed to cancel animation frame");
}

let handle = self
.window
.request_animation_frame(self.closure.as_ref().unchecked_ref())
.expect("Failed to request animation frame");

self.handle.set(Some(handle));
}
}

impl Drop for AnimationFrameHandler {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
self.window
.cancel_animation_frame(handle)
.expect("Failed to cancel animation frame");
}
}
}
16 changes: 15 additions & 1 deletion src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::super::WindowId;
use super::animation_frame::AnimationFrameHandler;
use super::event_handle::EventListenerHandle;
use super::media_query_handle::MediaQueryListHandle;
use super::pointer::PointerHandler;
Expand Down Expand Up @@ -37,6 +38,7 @@ pub struct Canvas {
on_dark_mode: Option<MediaQueryListHandle>,
pointer_handler: PointerHandler,
on_resize_scale: Option<ResizeScaleHandle>,
animation_frame_handler: AnimationFrameHandler,
}

pub struct Common {
Expand Down Expand Up @@ -87,7 +89,7 @@ impl Canvas {

Ok(Canvas {
common: Common {
window,
window: window.clone(),
raw: canvas,
old_size: Rc::default(),
current_size: Rc::default(),
Expand All @@ -105,6 +107,7 @@ impl Canvas {
on_dark_mode: None,
pointer_handler: PointerHandler::new(),
on_resize_scale: None,
animation_frame_handler: AnimationFrameHandler::new(window),
})
}

Expand Down Expand Up @@ -365,6 +368,13 @@ impl Canvas {
));
}

pub(crate) fn on_animation_frame<F>(&mut self, f: F)
where
F: 'static + FnMut(),
{
self.animation_frame_handler.on_animation_frame(f)
}

pub fn request_fullscreen(&self) {
self.common.request_fullscreen()
}
Expand All @@ -373,6 +383,10 @@ impl Canvas {
self.common.is_fullscreen()
}

pub fn request_animation_frame(&self) {
self.animation_frame_handler.request();
}

pub(crate) fn handle_scale_change<T: 'static>(
&self,
runner: &super::super::event_loop::runner::Shared<T>,
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/web/web_sys/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod animation_frame;
mod canvas;
pub mod event;
mod event_handle;
Expand Down
11 changes: 3 additions & 8 deletions src/platform_impl/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ pub struct Inner {
pub window: web_sys::Window,
canvas: Rc<RefCell<backend::Canvas>>,
previous_pointer: RefCell<&'static str>,
register_redraw_request: Box<dyn Fn()>,
destroy_fn: Option<Box<dyn FnOnce()>>,
}

Expand All @@ -39,8 +38,6 @@ impl Window {
attr: WindowAttributes,
platform_attr: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOE> {
let runner = target.runner.clone();

let id = target.generate_id();

let prevent_default = platform_attr.prevent_default;
Expand All @@ -49,8 +46,6 @@ impl Window {
let canvas = backend::Canvas::create(id, window.clone(), &attr, platform_attr)?;
let canvas = Rc::new(RefCell::new(canvas));

let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));

target.register(&canvas, id, prevent_default);

let runner = target.runner.clone();
Expand All @@ -64,7 +59,6 @@ impl Window {
window: window.clone(),
canvas,
previous_pointer: RefCell::new("auto"),
register_redraw_request,
destroy_fn: Some(destroy_fn),
})
.unwrap(),
Expand Down Expand Up @@ -106,8 +100,9 @@ impl Window {
}

pub fn request_redraw(&self) {
self.inner
.dispatch(|inner| (inner.register_redraw_request)());
self.inner.dispatch(move |inner| {
inner.canvas.borrow().request_animation_frame();
});
}

pub fn pre_present_notify(&self) {}
Expand Down
5 changes: 3 additions & 2 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,9 @@ impl Window {
///
/// - **iOS:** Can only be called on the main thread.
/// - **Android:** Subsequent calls after `MainEventsCleared` are not handled.
/// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`]
/// is used.
/// - **Wayland:** [`Event::RedrawRequested`] will be aligned with the frame callback
/// when [`Window::pre_present_notify`] is used.
/// - **Web:** [`Event::RedrawRequested`] will be aligned with the `requestAnimationFrame`.
///
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
/// [`Event::MainEventsCleared`]: crate::event::Event::MainEventsCleared
Expand Down

0 comments on commit 8558d09

Please sign in to comment.