← 中文资源

简体中文版本

可视视口

可视视口是屏幕上对布局视口的实际观察窗——捏合缩放、软键盘、浏览器工具栏改变的都是它,与媒体查询看到的布局视口不同。

阅读英文原文

概念定义

页面布局按布局视口计算,而用户真正看到的是在其上滑动的可视视口:被缩放、被偏移、被键盘或工具栏部分遮挡。两者恰好在定义移动端体验的交互中分道扬镳。

工作机制

window.visualViewport 暴露 scale、offsetLeft、offsetTop、宽高,以及 resize 与 scroll 事件;interactive-widget 元数据影响键盘是压缩还是覆盖可视视口。fixed 元素隶属布局视口,缩放时会滑出视野。

使用场景

把输入框保持在键盘上方的聊天应用、缩放下控件不丢的地图、与工具栏状态同步的全屏体验。移动端应优先监听 visualViewport 而非 window resize,并为桌面保留回退。

常见误区

各引擎键盘行为与 interactive-widget 取值不一:有的压缩布局视口,有的只覆盖。带小数的缩放值会让像素数学漂移,命中测试应经 getBoundingClientRect 取整。混合应用还有自己的浏览器 chrome,任何 Web API 都不会上报。

代码示例

/* Grid template areas */
.page-layout {
  display: grid;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
  grid-template-columns: 200px 1fr 250px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
  gap: 1rem;
}

.page-layout > header { grid-area: header; }
.page-layout > nav { grid-area: nav; }
.page-layout > main { grid-area: main; }
.page-layout > aside { grid-area: aside; }
.page-layout > footer { grid-area: footer; }

/* Auto-fit responsive grid */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1.5rem;
}

/* 12-column grid system */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1rem;
}

.grid-12 .col-6 { grid-column: span 6; }
.grid-12 .col-4 { grid-column: span 4; }
.grid-12 .col-3 { grid-column: span 3; }

/* Masonry-like layout */
.masonry {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-auto-rows: 10px;
  gap: 1rem;
}

.masonry .item-tall { grid-row: span 30; }
.masonry .item-short { grid-row: span 20; }

/* Responsive grid with media query */
@media (max-width: 768px) {
  .page-layout {
    grid-template-areas:
      "header"
      "nav"
      "main"
      "aside"
      "footer";
    grid-template-columns: 1fr;
  }
}
.container {
  width: 100%;
  padding: 1rem;
}

@media (min-width: 768px) {
  .container { max-width: 720px; }
}

@media (min-width: 1024px) {
  .container { max-width: 960px; }
}

@media (min-width: 1280px) {
  .container { max-width: 1200px; }
}