mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-23 22:58:00 -05:00
throttle scroll event
This commit is contained in:
42
src/browser/tests/window/scroll.html
Normal file
42
src/browser/tests/window/scroll.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<!-- Chrome don't scroll if the body isn't big enough. -->
|
||||
<body style=height:4000px;width:4000px></body>
|
||||
|
||||
<script id=scroll_evt>
|
||||
testing.async(async (restore) => {
|
||||
let scrollevt = 0;
|
||||
let scrollendevt = 0;
|
||||
await new Promise((resolve) => {
|
||||
document.addEventListener("scroll", (event) => {
|
||||
scrollevt++;
|
||||
});
|
||||
document.addEventListener("scrollend", (event) => {
|
||||
scrollendevt++;
|
||||
});
|
||||
|
||||
window.scrollTo(10, 20);
|
||||
testing.expectEqual(0, scrollevt);
|
||||
testing.expectEqual(0, scrollendevt);
|
||||
|
||||
// scroll immediately: the scroll event must be throttled.
|
||||
window.scrollTo(20, 40);
|
||||
testing.expectEqual(0, scrollevt);
|
||||
testing.expectEqual(0, scrollendevt);
|
||||
|
||||
// wait 10ms and scroll again: we should have a 2nd scroll, but no scrollend.
|
||||
window.setTimeout(() => {
|
||||
window.scrollTo(30, 40);
|
||||
}, 10);
|
||||
|
||||
// wait until scrollend happens.
|
||||
window.setTimeout(() => {
|
||||
resolve();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
restore();
|
||||
testing.expectEqual(2, scrollevt);
|
||||
testing.expectEqual(1, scrollendevt);
|
||||
});
|
||||
</script>
|
||||
@@ -63,7 +63,19 @@ _location: *Location,
|
||||
_timer_id: u30 = 0,
|
||||
_timers: std.AutoHashMapUnmanaged(u32, *ScheduleCallback) = .{},
|
||||
_custom_elements: CustomElementRegistry = .{},
|
||||
_scroll_pos: struct { x: u32, y: u32 } = .{ .x = 0, .y = 0 },
|
||||
_scroll_pos: struct {
|
||||
x: u32,
|
||||
y: u32,
|
||||
state: enum {
|
||||
scroll,
|
||||
end,
|
||||
done,
|
||||
},
|
||||
} = .{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.state = .done,
|
||||
},
|
||||
|
||||
pub fn asEventTarget(self: *Window) *EventTarget {
|
||||
return self._proto;
|
||||
@@ -387,18 +399,60 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
||||
},
|
||||
}
|
||||
|
||||
{
|
||||
// TODO According to the doc, scroll event should be throttled.
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event#scroll_event_throttling
|
||||
const event = try Event.init("scroll", .{ .bubbles = true }, page);
|
||||
try page._event_manager.dispatch(self._document.asEventTarget(), event);
|
||||
}
|
||||
self._scroll_pos.state = .scroll;
|
||||
|
||||
{
|
||||
// TODO scrollend must be dispatched once the scroll really ends.
|
||||
const event = try Event.init("scrollend", .{ .bubbles = true }, page);
|
||||
try page._event_manager.dispatch(self._document.asEventTarget(), event);
|
||||
}
|
||||
// We dispatch scroll event asynchronously after 10ms. So we can throttle
|
||||
// them.
|
||||
try page.scheduler.add(
|
||||
page,
|
||||
struct {
|
||||
fn dispatch(_page: *anyopaque) anyerror!?u32 {
|
||||
const p: *Page = @ptrCast(@alignCast(_page));
|
||||
const pos = &p.window._scroll_pos;
|
||||
// If the state isn't scroll, we can ignore safely to throttle
|
||||
// the events.
|
||||
if (pos.state != .scroll) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const event = try Event.init("scroll", .{ .bubbles = true }, p);
|
||||
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||
|
||||
pos.state = .end;
|
||||
|
||||
return null;
|
||||
}
|
||||
}.dispatch,
|
||||
10,
|
||||
.{ .low_priority = true },
|
||||
);
|
||||
// We dispatch scrollend event asynchronously after 20ms.
|
||||
try page.scheduler.add(
|
||||
page,
|
||||
struct {
|
||||
fn dispatch(_page: *anyopaque) anyerror!?u32 {
|
||||
const p: *Page = @ptrCast(@alignCast(_page));
|
||||
const pos = &p.window._scroll_pos;
|
||||
// Dispatch only if the state is .end.
|
||||
// If a scroll is pending, retry in 10ms.
|
||||
// If the state is .end, the event has been dispatched, so
|
||||
// ignore safely.
|
||||
switch (pos.state) {
|
||||
.scroll => return 10,
|
||||
.end => {},
|
||||
.done => return null,
|
||||
}
|
||||
const event = try Event.init("scrollend", .{ .bubbles = true }, p);
|
||||
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||
|
||||
pos.state = .done;
|
||||
|
||||
return null;
|
||||
}
|
||||
}.dispatch,
|
||||
20,
|
||||
.{ .low_priority = true },
|
||||
);
|
||||
}
|
||||
|
||||
const ScheduleOpts = struct {
|
||||
|
||||
Reference in New Issue
Block a user