Prevent perspective shifts when browser toolbar hides
When scrolling the page on mobile and the toolbar gets hidden or made visible again as a result of scrolling the browser reports the size of the page having changed when the scrolling ends, which in turn causes perspective shifts when the finger is lifted from the screen. Try to work around this by anticipating this resize and calculating the perspective as if the screen hadn't changed size. While window.visibleViewport and window.screen in theory could have been used to determine exactly where and when the browser toolbar is present, it was found through testing that neither Chrome nor Firefox on Android reports useful values through these APIs, and instead pretended that the inner viewport was the whole screen.
This commit is contained in:
parent
78d3f56672
commit
e5ba70d6a9
1 changed files with 52 additions and 9 deletions
|
@ -1,19 +1,61 @@
|
|||
function setViewportOffset(viewport) {
|
||||
// Calculate the distance from the top of the element to the center of the screen.
|
||||
// This is an accurate rendering, but we might want to change it for artistic effects.
|
||||
const windowHeight = window.innerHeight;
|
||||
let heightOffset = 0;
|
||||
let lastHeight = window.innerHeight;
|
||||
function getWindowHeight(event) {
|
||||
|
||||
/*
|
||||
When scrolling such that the browser toolbar is pushed out of view or
|
||||
pulled into view on a mobile device the height of the viewport changes.
|
||||
This causes the perspective to shift noticeably when you let go of your
|
||||
finger after scrolling. To avoid this, I store the last change of the
|
||||
window height if this was previously 0 and apply that to the calculated
|
||||
height of the window when the size changes.
|
||||
|
||||
This causes the heightOffset to toggle between 0 and the value that
|
||||
negates the perspective shift when you switch between having the browser
|
||||
toolbar visible and not visible on mobile, but stay as 0 in most other
|
||||
situations involving window resize.
|
||||
*/
|
||||
if (event?.type === 'resize') {
|
||||
if (heightOffset === 0 && lastHeight !== 0) {
|
||||
const newOffset = lastHeight - window.innerHeight;
|
||||
// Expect the browser toolbar resize to be between 20 and 100 pixels.
|
||||
if (Math.abs(newOffset) > 20 && Math.abs(newOffset) < 100) {
|
||||
heightOffset = newOffset;
|
||||
} else {
|
||||
heightOffset = 0;
|
||||
}
|
||||
} else {
|
||||
heightOffset = 0;
|
||||
}
|
||||
lastHeight = window.innerHeight;
|
||||
}
|
||||
return window.innerHeight + heightOffset;
|
||||
}
|
||||
|
||||
function setViewportOffset(viewport, windowHeight) {
|
||||
|
||||
/*
|
||||
Ignoring updates if the the bounding height of the viewport element is
|
||||
not inside of the visible client area on the page to optimise for when
|
||||
it not visible due to being off screen.
|
||||
*/
|
||||
const viewportRect = viewport.getBoundingClientRect();
|
||||
if (viewportRect.top > windowHeight || viewportRect.bottom < 0) {
|
||||
if (viewportRect.top > window.innerHeight || viewportRect.bottom < 0) {
|
||||
return;
|
||||
}
|
||||
const documentRect = document.documentElement.getBoundingClientRect();
|
||||
|
||||
/*
|
||||
Calculate the distance from the top of the viewport to the center of the
|
||||
screen. This is an accurate rendering of perspective shift as you
|
||||
scroll.
|
||||
|
||||
If the the bounds of a viewport element is close to the top or bottom
|
||||
of the page the perspective origin is adjusted so that it is still
|
||||
contained inside of the bounds of the element to not look out of place.
|
||||
This controls how much into the bounds the perspective origin should be
|
||||
endOffset controls how much into the bounds the perspective origin should be
|
||||
pushed when the screen in scrolled all the way to the top/bottom.
|
||||
*/
|
||||
const documentRect = document.documentElement.getBoundingClientRect();
|
||||
const endOffset = viewportRect.height * 0.25;
|
||||
const origin = Math.min(
|
||||
(viewportRect.bottom - documentRect.top) - endOffset,
|
||||
|
@ -27,9 +69,10 @@ function setViewportOffset(viewport) {
|
|||
}
|
||||
|
||||
const viewports = document.querySelectorAll(".viewport");
|
||||
function updateViewports() {
|
||||
function updateViewports(event) {
|
||||
const windowHeight = getWindowHeight(event);
|
||||
for (const viewport of viewports) {
|
||||
setViewportOffset(viewport);
|
||||
setViewportOffset(viewport, windowHeight);
|
||||
}
|
||||
}
|
||||
window.addEventListener("scroll", updateViewports, { passive: true });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue