Visual Viewport API

模板/侧边栏的 slug 无效:Visual Viewport

Visual Viewport API 提供了一种明确的机制,用于查询和修改窗口的 视觉视口 属性。视觉视口是屏幕上不包括屏幕键盘、捏合缩放区域外的区域或与页面尺寸不缩放的任何其他屏幕假象的可见部分。

概念与用法

移动 Web 包含两个视口:布局视口和视觉视口。布局视口涵盖页面上的所有元素,而视觉视口是屏幕上实际可见的部分。当用户捏合缩放页面时,视觉视口会缩小,但布局视口保持不变。屏幕键盘 (OSK) 等用户界面功能可以缩小视觉视口,而不影响布局视口。

当网页元素需要无论在屏幕可见部分的哪个位置都能显示时,会发生什么?例如,如果您需要一组图像控件在设备的捏合缩放级别下始终保持在屏幕上,该怎么办?当前浏览器处理此问题的方式各不相同。Visual Viewport API 使 Web 开发者可以通过相对于屏幕显示内容来定位元素来解决此问题。

要访问窗口的视觉视口,您可以通过 window.visualViewport 属性获取一个 VisualViewport 对象。该对象包含一组描述视口属性的属性。它还增加了三个事件:resizescrollscrollend。这些事件分别在视觉视口被调整大小、滚动以及完成滚动操作时触发。

前两个事件允许您相对于视觉视口进行元素定位,因为它是被滚动或缩放的,这通常会锚定到布局视口。scrollend 事件允许您在滚动操作完成后更新元素。例如,您可以使用这些事件来使元素在捏合缩放和滚动时固定在视觉视口上,并在滚动结束时更新它。

接口

VisualViewport

代表给定窗口的视觉视口。窗口的 VisualViewport 对象提供有关视口位置和大小的信息,并接收 resizescrollscrollend 事件。

其他接口的扩展

Window.visualViewport 只读

对窗口的 VisualViewport 对象的只读引用。如果此属性不存在,则表示 API 不受支持。

示例

我们的 Visual Viewport API 示例提供了视觉视口不同功能如何工作的基本演示,包括三种事件类型。在支持的桌面和移动浏览器中加载页面,然后尝试滚动页面和捏合缩放。在 resizescroll 事件上,信息框会被重新定位以保持相对于视觉视口的位置,并且其中显示的内容和滚动信息也会更新。此外,在 resizescroll 事件上,我们改变信息框的颜色以指示正在发生某些事情,并在 scrollend 事件上恢复原色。

您会发现,在桌面浏览器上,当窗口滚动时,Window.scrollXWindow.scrollY 值会更新——视觉视口位置不变。然而,在移动浏览器上,VisualViewport.offsetLeftVisualViewport.offsetTop 值通常会更新——通常是视觉视口发生变化,而不是窗口位置。

示例 HTML 如下所示。信息框由一个 idoutput<div> 表示。

html
<p id="instructions">
  Try scrolling around and pinch-zooming to see how the reported values change.
</p>
<div id="output">
  <p id="visual-info"></p>
  <hr />
  <p id="window-info"></p>
</div>

为简洁起见,我们不解释示例的 CSS — 它对于理解演示并不重要。您可以在上面的示例链接中查看它。

在 JavaScript 中,我们首先获取对我们将要更新的信息框的引用,以便在页面缩放和滚动时更新,以及其中包含的两个段落。第一个段落将包含报告的 VisualViewport.offsetLeftVisualViewport.offsetTop 值,而第二个段落将包含报告的 Window.scrollXWindow.scrollY 值。

js
const output = document.getElementById("output");
const visualInfo = document.getElementById("visual-info");
const windowInfo = document.getElementById("window-info");

接下来,我们定义当事件触发时将运行的两个关键函数。

  • scrollUpdater() 函数将在 resizescroll 事件上触发:此函数通过查询 VisualViewport.offsetTopVisualViewport.offsetLeft 属性并使用它们的值来更新相关 插入属性 的值,从而更新信息框相对于视觉视口的位置。我们还更改信息框的背景颜色以指示正在进行某些操作,并运行 updateText() 函数来更新框中显示的值。
  • scrollEndUpdater() 函数将在 scrollend 事件上触发:此函数将信息框恢复到其原始颜色,并运行 updateText() 函数以确保在 scrollend 事件上显示最新值。
js
const scrollUpdater = () => {
  output.style.top = `${visualViewport.offsetTop + 10}px`;
  output.style.left = `${visualViewport.offsetLeft + 10}px`;
  output.style.background = "yellow";
  updateText();
};

const scrollendUpdater = () => {
  output.style.background = "lime";
  updateText();
};

updateText() 函数如下所示 — 它将第一个段落的 HTMLElement.innerText 设置为显示当前的 VisualViewport.offsetLeftVisualViewport.offsetTop 值,并将第二个段落的 HTMLElement.innerText 设置为显示当前的 Window.scrollXWindow.scrollY 值。在定义 updateText() 之后,我们立即调用它,以便信息框在页面加载时正确显示。

js
function updateText() {
  visualInfo.innerText = `Visual viewport left: ${visualViewport.offsetLeft.toFixed(2)}
    top: ${visualViewport.offsetTop.toFixed(2)}`;
  windowInfo.innerText = `Window scrollX: ${window.scrollX.toFixed(2)}
    scrollY: ${window.scrollY.toFixed(2)}`;
}

updateText();

注意:我们使用 Number.toFixed() 方法将所有值截断到小数点后两位,因为某些浏览器会将其显示为亚像素值,可能带有大量小数位。

现在,我们在视觉视口和 Window 对象上设置事件处理程序属性,以便在适当的时间在移动和桌面设备上运行关键函数。

  • 我们在 window 上设置处理程序,以便在常规窗口滚动操作(例如在桌面浏览器上滚动页面)时,信息框的位置和内容会更新。
  • 我们在 visualViewport 上设置处理程序,以便在视觉视口滚动/缩放操作(例如在移动浏览器上滚动和捏合缩放页面)时,信息框的位置和内容会更新。
js
visualViewport.onresize = scrollUpdater;
visualViewport.onscroll = scrollUpdater;
visualViewport.onscrollend = scrollendUpdater;
window.onresize = scrollUpdater;
window.onscroll = scrollUpdater;
window.onscrollend = scrollendUpdater;

scrollUpdater() 将在 resizescroll 事件上触发,而 scrollEndUpdater() 将在 scrollend 事件上触发。

规范

规范
CSSOM 视图模块
# visualViewport

浏览器兼容性

api.VisualViewport

api.Window.visualViewport