我不喜欢Mobile Safari中的100vh,所以我用PostCSS解决了


CSS vhviewport units十分方便,因为您可以轻松创建适合屏幕的元素和与屏幕尺寸匹配的元素。

但是很遗憾,vh可能不适用于Mobile Safari。
这似乎是由于未考虑地址栏的高度而引起的。

各种解决方案

如果要100vh适应Mobile Safari中的屏幕,Stack Overflow提供了多种解决方案。

堆栈溢出-如何在移动Safari中修复vh(视口单元)css?

  • 使用视口单位Buggyfill
    我没有正确尝试,但似乎添加了CSS,以在运行时以vh单位覆盖值。

  • 与jQuery

    对应

    1
    $('.my-element').height(window.innerHeight + 'px');
  • 对应于JavaScript

    1
    document.getElementById("my-element").style.height = window.innerHeight + 'px';
  • height: 100%路线
    好老方法。

  • 对应于CSS自定义属性

    1
    2
    3
    4
    .my-element {
      height: 100vh; /* Fallback for browsers that do not support Custom Properties */
      height: calc(var(--vh, 1vh) * 100);
    }
    1
    2
    3
    4
    // First we get the viewport height and we multiple it by 1% to get a value for a vh unit
    let vh = window.innerHeight * 0.01;
    // Then we set the value in the --vh custom property to the root of the document
    document.documentElement.style.setProperty('--vh', `${vh}px`);

    CSS技巧-在移动

    上视口单位的技巧

  • 中,我个人认为最后一个CSS-Tricks解决方案很好,但是在我编写100vh的所有位置编写calc(var(--vh, 1vh) * 100);却很烦人。我想知道PostCSS是否会对此做些什么。那是未来的解决方案。

    CSS技巧-使用PostCSS在移动设备上视口单元的技巧

    制作PostCSS插件

    在本地创建自己的PostCSS插件。

    postcss-viewport-units-on-mobile.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    const postcss = require('postcss')

    // プラグイン定義
    module.exports = postcss.plugin('postcss-viewport-units-on-mobile', () =>
      createPlugin()
    )

    function createPlugin() {
      return root => {
        const replaces = []
        // CSSの宣言(declaration)を走査
        root.walkDecls(decl => {
          // vh,vmax,vminの単位で宣言した値を書き換えたCSS値を作る
          const newValue = decl.value.replace(
            /\b([-+]?[\d.]+)(vh|vmax|vmin)\b/g,
            replacer
          )
          if (decl.value !== newValue) {
            // vh,vmax,vminが書き換えられていたら、そのCSSを保持しておく
            replaces.push({ decl, newValue })
          }
        })
        // vh,vmax,vminが書き換えられたCSSの宣言を元のCSSの宣言の後ろに挿入していく
        for (const { decl, newValue } of replaces) {
          decl.parent.insertAfter(decl, decl.clone({ value: newValue }))
        }
      }
    }

    // vh,vmax,vminの書き換え
    function replacer(original, snum, unit) {
      if (isNaN(snum)) {
        return original
      }
      const num = snum - 0
      return `calc(${snum} * var(--${unit}, 1${unit}))`
    }

    当此PostCSS插件使用vh查找单位时,它将在其下添加用CSS自定义属性重写的CSS。

    所以这个CSS是

    before.css

    1
    2
    3
    4
    5
    6
    .a {
      height: 100vh;
    }
    .b {
      height: calc(100vh - 10px);
    }

    就像这样。

    after.css

    1
    2
    3
    4
    5
    6
    7
    8
    .a {
      height: 100vh;
      height: calc(100 * var(--vh, 1vh));
    }
    .b {
      height: calc(100vh - 10px);
      height: calc(calc(100 * var(--vh, 1vh)) - 10px);
    }

    放入PostCSS处理

    这取决于您如何使用PostCSS!我不会写一个,但是
    是以postcss.config.js编写的形式吗?

    postcss.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    module.exports = {
      plugins: [
        // ...
        // require('autoprefixer'),
        // ...
        require('./path/to/postcss-viewport-units-on-mobile.js')(), // 作ったプラグイン
        // ...
      ]
    }

    将JavaScript嵌入您的应用程序

    在您的应用程序中嵌入以下JavaScript。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    updateViewport()

    window.addEventListener('resize', updateViewport)

    function updateViewport() {
      const vh = window.innerHeight / 100
      const vw = window.innerWidth / 100

      const root = document.documentElement

      // 各カスタムプロパティに`window.innerHeight / 100`,`window.innerWidth / 100`の値をセット
      root.style.setProperty('--vh', `${vh}px`)
      if (vh > vw) {
        root.style.setProperty('--vmax', `${vh}px`)
        root.style.setProperty('--vmin', `${vw}px`)
      } else {
        root.style.setProperty('--vmax', `${vw}px`)
        root.style.setProperty('--vmin', `${vh}px`)
      }
    }

    执行结果

    描述的CSS

    1
    2
    3
    .my-element {
      height: 100vh;
    }

    转换如下,

    1
    2
    3
    4
    .my-element {
      height: 100vh;
      height: calc(100 * var(--vh, 1vh));
    }

    在JavaScript中,如果将CSS自定义属性--vh设置为window.innerHeight / 100,则
    .my-element的高度与window.innerHeight的高度相同,即使在Mobile Safari中也可以很好地显示在屏幕上!

    概括

    您可以使用CSS技巧-该技巧可以在移动技巧上视口单元,而不必自己重写CSS。是的,使用PostCSS。