LibWeb: Account for visual viewport when scrolling a target into view
This ensures the correct portion of the screen is scrolled into view when the window is pinch-zoomed.
This commit is contained in:
parent
5440f0797b
commit
a87f7b9561
3 changed files with 56 additions and 3 deletions
|
|
@ -50,6 +50,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/VisualViewport.h>
|
||||
#include <LibWeb/DOM/AbstractElement.h>
|
||||
#include <LibWeb/DOM/Attr.h>
|
||||
#include <LibWeb/DOM/DOMTokenList.h>
|
||||
|
|
@ -2896,9 +2897,16 @@ static CSSPixelPoint determine_the_scroll_into_view_position(Element& target, Bi
|
|||
CSSPixelRect scrolling_box_rect;
|
||||
CSSPixelPoint current_scroll_position;
|
||||
if (scrolling_box.is_document()) {
|
||||
// NOTE: Element::getBoundingClientRect() returns coordinates relative to the viewport.
|
||||
scrolling_box_rect = { {}, scrolling_box.document().viewport_rect().size() };
|
||||
current_scroll_position = scrolling_box.document().navigable()->viewport_scroll_offset();
|
||||
auto& document = scrolling_box.document();
|
||||
auto& visual_viewport = *document.visual_viewport();
|
||||
// NB: Use the visual viewport as the scrolling box, this ensures that the target is scrolled into the visible
|
||||
// region on screen when the page is pinch-zoomed.
|
||||
CSSPixelSize visible_size {
|
||||
CSSPixels::nearest_value_for(visual_viewport.width()),
|
||||
CSSPixels::nearest_value_for(visual_viewport.height()),
|
||||
};
|
||||
scrolling_box_rect = { visual_viewport.offset(), visible_size };
|
||||
current_scroll_position = document.navigable()->viewport_scroll_offset() + visual_viewport.offset();
|
||||
} else if (auto paintable_box = scrolling_box.paintable_box()) {
|
||||
current_scroll_position = paintable_box->scroll_offset();
|
||||
scrolling_box_rect = paintable_box->absolute_rect();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
target visible before scrollIntoView: false
|
||||
target visible after scrollIntoView: true
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#target {
|
||||
position: absolute;
|
||||
top: 450px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
height: 3000px;
|
||||
}
|
||||
</style>
|
||||
<div id="target"></div>
|
||||
<div class="spacer"></div>
|
||||
<script src="include.js"></script>
|
||||
<script>
|
||||
// scrollIntoView() must account for the visual viewport when the page is pinch-zoomed: a target inside the
|
||||
// layout viewport but below the smaller visible region must still be scrolled into view.
|
||||
test(() => {
|
||||
const visualViewport = window.visualViewport;
|
||||
const target = document.getElementById("target");
|
||||
|
||||
// getBoundingClientRect() is relative to the layout viewport; the visible region is the visual viewport,
|
||||
// which starts at offsetTop and is height tall.
|
||||
function isTargetVisible() {
|
||||
const rect = target.getBoundingClientRect();
|
||||
return rect.top >= visualViewport.offsetTop - 0.5
|
||||
&& rect.bottom <= visualViewport.offsetTop + visualViewport.height + 0.5;
|
||||
}
|
||||
|
||||
// Zoom in near the top so the visible region only covers the top of the page.
|
||||
internals.pinch(10, 10, 1.0);
|
||||
println(`target visible before scrollIntoView: ${isTargetVisible()}`);
|
||||
|
||||
target.scrollIntoView({ block: "nearest", behavior: "instant" });
|
||||
println(`target visible after scrollIntoView: ${isTargetVisible()}`);
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue