Skip to content

可选运行时

English · 简体中文

编译器本身不产生任何 JavaScript。这个模块是单独的入口,默认不需要,只有当 CSS 的视口单位和用户看到的可视区域对不上时才引入。

js
import { observeAdaptiveViewport } from 'postcss-adaptive-matrix/runtime'

const observer = observeAdaptiveViewport()
// 组件卸载 / 页面离开时
observer.destroy()

什么时候需要它

CSS 的 vw / vh 指的是布局视口,浏览器有意让它在软键盘弹出、页面被捏合缩放时保持不动。多数情况这是对的行为。但有几种场景下它不是:

场景现象
移动端软键盘弹起100vh 仍是全屏高,底部按钮被键盘盖住
用户双指捏合缩放视口单位不变,固定元素飘出屏幕
iOS Safari 地址栏收起/展开100vh 与实际可视高度差一条地址栏
WebView / Capacitor / Tauri 外壳宿主给的可视区域与布局视口不一致

这些都是 VisualViewport 才能看到的信息,CSS 拿不到。这个观察器把它读出来写成 CSS 变量。

发布的变量

调用后写在 document.documentElement 上(可用 target 改):

变量含义
--adaptive-width可视区域宽度(px 数值,无单位)
--adaptive-height可视区域高度
--adaptive-layout-height布局视口高度,即 window.innerHeight
--adaptive-keyboard-height软键盘遮挡高度,无键盘时为 0
--adaptive-scale当前捏合缩放倍数
--adaptive-vh可视高度的 1%,带 px 单位
--adaptive-vw可视宽度的 1%,带 px 单位

前五个是裸数值,要参与计算得自己加单位(calc(var(--adaptive-keyboard-height) * 1px));后两个已经带单位,可以直接当 vh / vw 的替代品用。

典型用法

真·全屏高度,不受地址栏影响:

css
.screen {
  min-block-size: calc(var(--adaptive-vh, 1vh) * 100);
}

回退值 1vh 很重要——运行时没加载、或者在 SSR 首屏时,样式依然成立。

底部操作栏避开软键盘:

css
.action-bar {
  position: fixed;
  inset-block-end: calc(var(--adaptive-keyboard-height, 0) * 1px);
}

选项

ts
observeAdaptiveViewport({
  prefix: 'adaptive',        // 变量前缀,写不写开头的 -- 都行
  target: document.documentElement,
  window: globalThis.window, // 多窗口 / 测试时注入
  document: globalThis.document,
})

返回:

ts
interface AdaptiveViewportObserver {
  update(): AdaptiveViewportSnapshot | null   // 手动触发一次,返回本次读数
  destroy(): void                             // 解绑全部监听
}

SSR

没有 window 时构造函数不报错,返回的观察器什么也不做,update() 返回 null。所以可以无条件在模块顶层调用,不需要包 if (typeof window !== 'undefined')

但服务端渲染的首屏 HTML 里不会有这些变量,因此每处使用都要写回退值,否则首帧会拿到空值。

开销

更新走 requestAnimationFrame 合并,一帧最多写一次;监听全部是 passive。不使用时不引入——它是独立入口,不会被主包带进去。

基于 MIT 协议发布