概念定义
页面布局按布局视口计算,而用户真正看到的是在其上滑动的可视视口:被缩放、被偏移、被键盘或工具栏部分遮挡。两者恰好在定义移动端体验的交互中分道扬镳。
工作机制
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; }
}