为什么 ScrollTop 位置在移动版 Safari 上不起作用?
Why does ScrollTop position not work on mobile Safari?
我的网站上有一个功能,徽标会根据用户在网站上的位置垂直滚动。
您可以看到它在 Chrome here
上运行
但是它不适用于 Safari,包括手机和平板电脑。
滚动位置在控制台中似乎根本没有改变。
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = document.documentElement.scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = document.documentElement.scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
此行为背后的原因在于浏览器之间滚动实现的差异。例如 Chrome 根据 <html>
计算页面滚动,而 Safari 在 <body>
.
上做同样的事情
Chrome:
野生动物园:
考虑到此信息,可以合理地假设在 Safari 中 document.documentElement
完全不知道页面全局滚动值。
要解决此问题,您可以定义一个辅助函数,它使用 document.scrollingElement
并回退到 document.documentElement
上的 getBoundingClientRect
:
function getScrollingElement() {
if (document.scrollingElement) {
return document.scrollingElement;
}
const docElement = document.documentElement;
const docElementRect = docElement.getBoundingClientRect();
return {
scrollHeight: Math.ceil(docElementRect.height),
scrollTop: Math.abs(docElementRect.top)
}
}
并在您的 update
函数中使用它:
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = getScrollingElement().scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = getScrollingElement().scrollTop;
// ...
}
function getScrollingElement() {
// ...
}
完整代码:
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = getScrollingElement().scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = getScrollingElement().scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function getScrollingElement() {
if (document.scrollingElement) {
return document.scrollingElement;
}
const docElement = document.documentElement;
const docElementRect = docElement.getBoundingClientRect();
return {
scrollHeight: Math.ceil(docElementRect.height),
scrollTop: Math.abs(docElementRect.top)
}
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
希望对您有所帮助。
我的网站上有一个功能,徽标会根据用户在网站上的位置垂直滚动。
您可以看到它在 Chrome here
上运行但是它不适用于 Safari,包括手机和平板电脑。
滚动位置在控制台中似乎根本没有改变。
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = document.documentElement.scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = document.documentElement.scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
此行为背后的原因在于浏览器之间滚动实现的差异。例如 Chrome 根据 <html>
计算页面滚动,而 Safari 在 <body>
.
Chrome:
野生动物园:
考虑到此信息,可以合理地假设在 Safari 中 document.documentElement
完全不知道页面全局滚动值。
要解决此问题,您可以定义一个辅助函数,它使用 document.scrollingElement
并回退到 document.documentElement
上的 getBoundingClientRect
:
function getScrollingElement() {
if (document.scrollingElement) {
return document.scrollingElement;
}
const docElement = document.documentElement;
const docElementRect = docElement.getBoundingClientRect();
return {
scrollHeight: Math.ceil(docElementRect.height),
scrollTop: Math.abs(docElementRect.top)
}
}
并在您的 update
函数中使用它:
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = getScrollingElement().scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = getScrollingElement().scrollTop;
// ...
}
function getScrollingElement() {
// ...
}
完整代码:
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = getScrollingElement().scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = getScrollingElement().scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function getScrollingElement() {
if (document.scrollingElement) {
return document.scrollingElement;
}
const docElement = document.documentElement;
const docElementRect = docElement.getBoundingClientRect();
return {
scrollHeight: Math.ceil(docElementRect.height),
scrollTop: Math.abs(docElementRect.top)
}
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
希望对您有所帮助。