CSS `Environment Variables` (`env()`) 结合 `safe-area-inset` 等系统级信息

各位好,欢迎来到今天的“CSS Environment Variables & Safe Area Insets” 讲座。今天咱们就一起扒一扒CSS这位老兄的新玩具,看看它能玩出什么花样,尤其是跟那些“安全区域”搅和在一起的时候。准备好了吗?咱们这就开始!

开场白:CSS 的“环境意识”觉醒

话说CSS一直以来都像个专注的艺术家,埋头苦干,只管把样式画出来,至于它画在什么环境里,那可不管。但时代变了,现在是响应式设计的天下,CSS也得有点“环境意识”才行。 这就引出了咱们今天的主角之一:env() 函数,还有它的小伙伴 safe-area-inset-*

第一部分:env() 函数:CSS 的千里眼

env() 函数,顾名思义,就是用来读取“环境变量”的。这里的“环境变量”不是指操作系统里的那些,而是由浏览器或者宿主环境(比如你的App WebView)提供的一些信息。

  • 语法:

    property: env(variable-name, fallback-value);
    • variable-name:你要读取的环境变量的名字。
    • fallback-value:可选参数,如果环境变量不存在,就用这个值。 就像你问别人借钱,最好先想好如果借不到怎么办。
  • 例子:

    body {
      background-color: env(theme-color, white); /* 如果有theme-color就用它,没有就用白色 */
    }

    这个例子里,如果浏览器或者宿主环境定义了 theme-color 这个环境变量,那么 body 的背景色就会是那个颜色。否则,就是白色。

  • 浏览器支持:

    env() 的浏览器支持情况还算不错,但也不是所有浏览器都支持。使用前最好查一下 Can I Use,或者用 @supports 特性查询来做兼容性处理。

    @supports (background-color: env(safe-area-inset-top)) {
        /* 只有在支持 env() 的浏览器里才执行这里的代码 */
        body {
            padding-top: env(safe-area-inset-top);
        }
    }

*第二部分:`safe-area-inset-`:那些年,我们躲过的刘海**

现在,重头戏来了!safe-area-inset-* 是一组预定义的环境变量,专门用来获取“安全区域”的信息。

  • 什么是安全区域?

    简单来说,安全区域就是屏幕上没有被系统UI元素(比如状态栏、导航栏、刘海、虚拟按键)遮挡的区域。 想象一下,你的手机屏幕上有个刘海,或者底部有个虚拟按键栏,你的内容要显示在这些东西的“安全范围”之内,才不会被遮住。

  • *`safe-area-inset-` 变量:**

    变量名 含义
    safe-area-inset-top 安全区域距离屏幕顶部的距离。
    safe-area-inset-right 安全区域距离屏幕右侧的距离。
    safe-area-inset-bottom 安全区域距离屏幕底部的距离。
    safe-area-inset-left 安全区域距离屏幕左侧的距离。

    这些变量的值都是长度值,比如 px, em, rem 等。

  • 用法示例:

    body {
      padding-top: env(safe-area-inset-top, 0px); /* 顶部安全区域的内边距 */
      padding-right: env(safe-area-inset-right, 0px); /* 右侧安全区域的内边距 */
      padding-bottom: env(safe-area-inset-bottom, 0px); /* 底部安全区域的内边距 */
      padding-left: env(safe-area-inset-left, 0px); /* 左侧安全区域的内边距 */
    }
    
    .container {
      position: fixed;
      bottom: 0;
      left: 0;
      width: 100%;
      padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 10px); /* 底部固定定位元素的内边距,加上10px的额外间距 */
    }

    上面的例子里,我们给 body 加上了安全区域的内边距,这样内容就不会被状态栏或者刘海遮挡了。 还有一个例子是给底部固定定位的元素加上内边距,保证它不会被虚拟按键栏遮挡,并且额外增加了10px的间距。

  • constant() 函数(已废弃):

    env() 出现之前,iOS Safari 提供了一个类似的函数叫做 constant(),用来获取安全区域的信息。 但是 constant() 已经被废弃了,现在应该使用 env()safe-area-inset-*

    • 注意: 不要再使用 constant() 了!

第三部分:实战演练:打造安全感满满的页面

光说不练假把式,咱们来几个实际的例子,看看怎么用 env()safe-area-inset-* 来打造安全感满满的页面。

  • 场景一:全屏图片

    假设你有一个全屏显示的图片,但是不想让它被状态栏或者刘海遮挡。

    <!DOCTYPE html>
    <html>
    <head>
      <title>全屏图片</title>
      <style>
        body {
          margin: 0;
          height: 100vh; /* 确保 body 占据整个视口高度 */
          overflow: hidden; /* 隐藏滚动条 */
        }
    
        .full-screen-image {
          width: 100%;
          height: 100%;
          object-fit: cover; /* 保持图片宽高比并填充整个容器 */
          padding-top: env(safe-area-inset-top, 0px); /* 关键:顶部安全区域内边距 */
          padding-bottom: env(safe-area-inset-bottom, 0px); /* 底部安全区域内边距 */
        }
      </style>
    </head>
    <body>
      <img class="full-screen-image" src="your-image.jpg" alt="全屏图片">
    </body>
    </html>

    在这个例子里,我们给 img 元素加上了顶部和底部的安全区域内边距,这样图片就不会被状态栏或者刘海遮挡了。 object-fit: cover; 保证了图片能够填充整个容器,并且保持宽高比。

  • 场景二:固定底部导航栏

    假设你有一个固定在底部的导航栏,但是不想让它被虚拟按键栏遮挡。

    <!DOCTYPE html>
    <html>
    <head>
      <title>底部导航栏</title>
      <style>
        body {
          margin: 0;
          padding-bottom: 50px; /* 预留底部导航栏的空间 */
        }
    
        .bottom-nav {
          position: fixed;
          bottom: 0;
          left: 0;
          width: 100%;
          height: 50px;
          background-color: #f0f0f0;
          display: flex;
          justify-content: space-around;
          align-items: center;
          padding-bottom: env(safe-area-inset-bottom, 0px); /* 关键:底部安全区域内边距 */
        }
    
        .bottom-nav a {
          text-decoration: none;
          color: #333;
        }
      </style>
    </head>
    <body>
      <h1>Some Content</h1>
      <p>More Content...</p>
    
      <nav class="bottom-nav">
        <a href="#">Home</a>
        <a href="#">Search</a>
        <a href="#">Profile</a>
      </nav>
    </body>
    </html>

    在这个例子里,我们给 nav 元素加上了底部的安全区域内边距,这样导航栏就不会被虚拟按键栏遮挡了。 同时,我们给 body 预留了 50px 的底部空间,避免内容被导航栏覆盖。

  • 场景三:聊天气泡

    很多聊天应用的气泡会贴着屏幕底部显示,如果底部有虚拟按键,就需要考虑安全区域。

    .chat-bubble {
        position: absolute;
        bottom: 10px;
        left: 10px;
        background-color: #eee;
        padding: 10px;
        border-radius: 10px;
        margin-bottom: env(safe-area-inset-bottom, 0px); /* 底部安全区域外边距,避免被虚拟按键遮挡 */
    }

    这样,聊天气泡就会自动避开底部虚拟按键区域,保证用户体验。

第四部分:注意事项和高级技巧

  • viewport-fit=cover

    要让 safe-area-inset-* 生效,需要在 <meta> 标签里设置 viewport-fit=cover

    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">

    viewport-fit=cover 会让网页内容充满整个屏幕,包括安全区域。 如果不设置这个,safe-area-inset-* 的值可能都是 0。

  • 配合 calc() 使用:

    safe-area-inset-* 的值可以和其他长度值进行计算,比如:

    .element {
      padding-top: calc(env(safe-area-inset-top, 0px) + 20px); /* 顶部安全区域内边距加上20px */
    }
  • 动态更新:

    安全区域的值可能会动态变化,比如用户旋转屏幕,或者显示/隐藏虚拟按键。 所以,最好使用 CSS 变量,并用 JavaScript 来更新这些变量的值。

    function updateSafeAreaVariables() {
        document.documentElement.style.setProperty('--safe-area-top', `${window.safeAreaInsets.top}px`);
        document.documentElement.style.setProperty('--safe-area-bottom', `${window.safeAreaInsets.bottom}px`);
        document.documentElement.style.setProperty('--safe-area-left', `${window.safeAreaInsets.left}px`);
        document.documentElement.style.setProperty('--safe-area-right', `${window.safeAreaInsets.right}px`);
    }
    
    window.addEventListener('resize', updateSafeAreaVariables);
    updateSafeAreaVariables(); // 初始化时也调用一次

    然后在 CSS 中使用这些变量:

    body {
        padding-top: env(--safe-area-top, 0px);
        padding-bottom: env(--safe-area-bottom, 0px);
        padding-left: env(--safe-area-left, 0px);
        padding-right: env(--safe-area-right, 0px);
    }

    注意: 上面的 window.safeAreaInsets 只是一个示例,具体的 API 取决于你的宿主环境(比如 WebView)。

  • 测试:

    在不同的设备和浏览器上进行测试,确保你的页面在各种情况下都能正常显示。 可以使用浏览器开发者工具的“设备模式”来模拟不同的屏幕尺寸和设备。

  • 优雅降级:

    由于 env()safe-area-inset-* 的浏览器支持情况不一致,要做好优雅降级。 可以使用 fallback-value 或者 @supports 特性查询来提供备用方案。

    body {
      padding-top: env(safe-area-inset-top, 20px); /* 如果不支持,就用20px的内边距 */
    }
    
    @supports not (padding-top: env(safe-area-inset-top)) {
      body {
        padding-top: 20px; /* 如果完全不支持 env(),就用20px */
      }
    }

第五部分:总结与展望

今天咱们一起学习了 CSS 的 env() 函数和 safe-area-inset-* 变量,了解了它们的作用、用法和注意事项。 它们是现代 Web 开发中非常重要的工具,可以帮助我们打造更好的用户体验,让我们的页面在各种设备上都能完美显示。

随着 Web 技术的不断发展,CSS 也会变得越来越强大, 相信未来会有更多的“环境变量”出现, 让我们拭目以待!

结束语:

感谢大家的聆听,希望今天的讲座对你有所帮助。 如果你还有任何问题,欢迎随时提问。 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注