CSS 视口单位动态计算:浏览器 UI 栏收缩对 dvh 与 svh 的实时重算
大家好,今天我们来深入探讨一个在现代 Web 开发中日益重要的主题:CSS 视口单位的动态计算,特别是当浏览器 UI 栏(如地址栏、底部导航栏等)收缩或展开时,dvh 和 svh 这两个单位的实时重算。
什么是视口单位?
在讨论 dvh 和 svh 之前,我们先回顾一下 CSS 中常见的视口单位:
vw(viewport width): 视口宽度的 1%。vh(viewport height): 视口高度的 1%。vi(viewport inline size): 视口内联尺寸的 1%。 在水平书写模式下,等同于vw。vb(viewport block size): 视口块尺寸的 1%。 在水平书写模式下,等同于vh。vmin(viewport minimum): 视口宽度和高度中较小值的 1%。vmax(viewport maximum): 视口宽度和高度中较大值的 1%。
这些单位提供了一种相对于浏览器视口大小来设置元素大小的方式,使得布局更加灵活和响应式。然而,传统的 vh 单位存在一个问题:它基于视口初始高度计算,不考虑动态变化的浏览器 UI 栏。这意味着当用户滚动页面,UI 栏收缩时,使用 vh 单位设置的元素高度可能超出实际可见区域,导致内容被遮挡。
dvh 和 svh:更智能的视口高度单位
为了解决 vh 的局限性,CSS 引入了 dvh (dynamic viewport height) 和 svh (small viewport height) 这两个新的视口高度单位。
dvh(dynamic viewport height): 动态视口高度的 1%。它会随着浏览器 UI 栏的显示和隐藏而动态调整,始终反映当前可见视口的最大高度。svh(small viewport height): 最小视口高度的 1%。它基于 UI 栏完全展开时的视口高度计算,代表了当前环境下的最小可用视口高度。
dvh 和 svh 之间的关系可以用以下表格概括:
| 单位 | 描述 | 适用场景 |
|---|---|---|
vh |
视口初始高度的 1% | 适用于不需要考虑浏览器 UI 栏动态变化的场景,或者在不支持 dvh 和 svh 的旧浏览器中作为回退方案。 |
dvh |
动态视口最大高度的 1% | 适用于需要占据整个可见视口,并随着浏览器 UI 栏变化而动态调整的元素,例如全屏背景、固定定位的元素等。 |
svh |
UI 栏完全展开时视口高度(最小高度)的 1% | 适用于需要确保元素始终在最小可用视口内可见的场景,例如在移动设备上,防止内容被底部导航栏遮挡。 |
lvh |
逻辑视口最大高度的 1% | 适用于逻辑像素计算的视口,和dvh类似,但主要区分逻辑像素和物理像素。 |
除了dvh和svh外,还有lvh等逻辑视口单位,他们和dvh的区别主要在于计算的逻辑像素而不是物理像素。
浏览器 UI 栏收缩对 dvh 和 svh 的影响
当浏览器 UI 栏收缩时,dvh 的值会增大,因为它反映了当前可见视口的最大高度。而 svh 的值保持不变,因为它基于 UI 栏完全展开时的视口高度计算。
以下面的例子说明:
假设初始视口高度(UI 栏完全展开)为 800px,UI 栏收缩后的视口高度为 900px。
100vh始终等于 800px。100dvh在 UI 栏展开时等于 800px,在 UI 栏收缩时等于 900px。100svh始终等于 800px。
如何使用 dvh 和 svh?
在 CSS 中,你可以像使用其他视口单位一样使用 dvh 和 svh。例如:
.full-height-element {
height: 100dvh; /* 占据整个可见视口的高度,并随着 UI 栏变化而动态调整 */
}
.safe-area-element {
height: 100svh; /* 确保元素始终在最小可用视口内可见 */
}
dvh 和 svh 的实际应用场景
-
全屏背景: 使用
height: 100dvh可以创建一个占据整个可见视口的背景,即使浏览器 UI 栏收缩,背景也能始终覆盖整个屏幕。.full-screen-background { position: fixed; top: 0; left: 0; width: 100vw; height: 100dvh; background-image: url("your-image.jpg"); background-size: cover; z-index: -1; /* 将背景放在内容后面 */ } -
固定定位的页眉/页脚: 使用
height: 100dvh或height: 100svh可以确保固定定位的页眉或页脚始终占据正确的视口高度,不会被浏览器 UI 栏遮挡。.header { position: fixed; top: 0; left: 0; width: 100vw; height: 10vh; /* 或者使用固定像素值 */ background-color: #fff; z-index: 10; } .footer { position: fixed; bottom: 0; left: 0; width: 100vw; height: 10svh; /* 确保底部导航栏不会遮挡页脚 */ background-color: #fff; z-index: 10; } .content { margin-top: 10vh; /* 为页眉留出空间 */ margin-bottom: 10svh; /* 为页脚留出空间 */ } -
移动应用中的安全区域: 在移动应用中,可以使用
height: 100svh来确保内容始终在安全区域内可见,防止被状态栏、导航栏或其他 UI 元素遮挡。.safe-area { padding-top: env(safe-area-inset-top); /* 使用环境变量获取安全区域的内边距 */ padding-bottom: env(safe-area-inset-bottom); height: 100svh; } -
模态框/对话框: 使用
dvh可以设置模态框的高度,确保在UI栏变化时,模态框始终占据整个可见视口,提供更好的用户体验。.modal { position: fixed; top: 0; left: 0; width: 100vw; height: 100dvh; background-color: rgba(0, 0, 0, 0.5); /* 半透明背景 */ display: flex; justify-content: center; align-items: center; z-index: 1000; } .modal-content { background-color: #fff; padding: 20px; border-radius: 5px; }
dvh 和 svh 的兼容性
截至目前(2024年),dvh 和 svh 的兼容性已经相当不错,主流浏览器都支持这些单位。然而,为了确保在旧浏览器中的兼容性,建议使用 vh 作为回退方案,并使用 CSS 的 @supports 特性查询来判断浏览器是否支持 dvh 和 svh。
.full-height-element {
height: 100vh; /* 回退方案 */
}
@supports (height: 100dvh) {
.full-height-element {
height: 100dvh; /* 支持 dvh 的浏览器使用 dvh */
}
}
.safe-area-element {
height: 100vh; /* 回退方案 */
}
@supports (height: 100svh) {
.safe-area-element {
height: 100svh; /* 支持 svh 的浏览器使用 svh */
}
}
或者,可以使用 JavaScript 来检测 dvh 和 svh 的支持情况,并动态地更新 CSS 变量。
function supportsDvhSvh() {
return CSS.supports('height', '100dvh') && CSS.supports('height', '100svh');
}
if (supportsDvhSvh()) {
console.log('dvh and svh are supported!');
} else {
console.log('dvh and svh are not supported.');
// 提供回退方案,例如使用 vh 或者 JavaScript 来计算高度
}
使用 JavaScript 监听视口变化并动态调整
即使浏览器支持 dvh 和 svh,有时也可能需要使用 JavaScript 来监听视口变化,并根据需要动态调整元素的大小。例如,你可能需要根据设备的屏幕方向、键盘的显示状态或其他因素来调整布局。
可以使用 ResizeObserver API 来监听元素大小的变化,包括视口大小的变化。
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const viewportHeight = window.innerHeight;
document.documentElement.style.setProperty('--viewport-height', `${viewportHeight}px`);
}
});
resizeObserver.observe(document.documentElement); // 监听根元素的大小变化
// 在 CSS 中使用 CSS 变量
/*
.element {
height: var(--viewport-height);
}
*/
在这个例子中,我们创建了一个 ResizeObserver 实例,并监听根元素(document.documentElement)的大小变化。当视口大小发生变化时,ResizeObserver 会触发回调函数,我们可以在回调函数中获取当前视口的高度,并将其设置为 CSS 变量。然后,我们可以在 CSS 中使用这个 CSS 变量来设置元素的高度。
潜在问题和注意事项
-
过度使用
dvh: 虽然dvh很有用,但过度使用可能会导致布局不稳定,因为元素的大小会随着浏览器 UI 栏的变化而不断调整。 谨慎使用,并确保布局在各种情况下都能正常工作。 -
性能问题: 频繁地计算和更新元素的大小可能会影响性能,特别是在复杂的布局中。 尽量减少不必要的计算,并使用性能分析工具来优化代码。
-
与其他 CSS 属性的冲突:
dvh和svh可能会与其他 CSS 属性(如min-height、max-height等)发生冲突。 确保理解这些属性之间的优先级关系,并避免出现意外的结果。 -
overflow属性: 在使用dvh设置元素高度时,需要注意overflow属性的设置。 如果元素的内容超出其高度,可能会导致内容被裁剪或出现滚动条。
总结一下
dvh 和 svh 是 CSS 中非常有用的视口高度单位,可以帮助我们创建更加灵活和响应式的布局,特别是当需要考虑浏览器 UI 栏的动态变化时。 通过充分理解这些单位的特性和使用场景,我们可以构建更好的用户体验。记住,兼容性是关键,务必提供合理的降级方案。
兼容性与优雅降级策略
确保你的代码能够在各种浏览器环境下正常运行,为不支持 dvh 和 svh 的浏览器提供回退方案。
考虑性能影响
避免过度使用 dvh 和 svh,特别是在复杂的布局中。 使用性能分析工具来优化代码,确保页面流畅运行。
动态调整需要谨慎
虽然 JavaScript 可以动态调整元素的大小,但也需要谨慎使用,避免出现布局抖动或性能问题。
更多IT精英技术系列讲座,到智猿学院