Html CSS3 100vh 在移动浏览器中不是恒定的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37112218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 13:15:04  来源:igfitidea点击:

CSS3 100vh not constant in mobile browser

htmlcssviewport-units

提问by Nereo Costacurta

I have a very odd issue... in every browser and mobile version I encountered this behavior:

我有一个非常奇怪的问题......在我遇到过这种行为的每个浏览器和移动版本中:

  • all the browsers have a top menu when you load the page (showing the address bar for example) which slide up when you start scrolling the page.
  • 100vh sometimesis calculated only on the visible part of a viewport, so when the browser bar slide up 100vh increases (in terms of pixels)
  • all layout re-paint and re-adjust since the dimensions have changed
  • a bad jumpy effect for user experience
  • 当您加载页面(例如显示地址栏)时,所有浏览器都有一个顶部菜单,当您开始滚动页面时会向上滑动。
  • 100vh有时仅在视口的可见部分计算,因此当浏览器栏向上滑动时 100vh 增加(以像素为单位)
  • 由于尺寸发生变化,所有布局都重新绘制和重新调整
  • 对用户体验的不良跳跃效果

How can avoid this problem? When I first heard of viewport-height I was excited and I thought I could use it for fixed height blocks instead of using javascript, but now I think the only way to do that is in fact javascript with some resize event...

如何避免这个问题?当我第一次听说 viewport-height 时,我很兴奋,我认为我可以将它用于固定高度的块而不是使用 javascript,但现在我认为唯一的方法实际上是使用带有一些调整大小事件的 javascript ......

you can see the problem at: sample site

您可以在以下位置查看问题:示例站点

Can anyone help me with / suggest a CSS solution?

任何人都可以帮助我/建议 CSS 解决方案吗?



simple test code:

简单的测试代码:

/* maybe i can track the issue whe it occours... */
$(function(){
  var resized = -1;
  $(window).resize(function(){
    $('#currenth').val( $('.vhbox').eq(1).height() );
    if (++resized) $('#currenth').css('background:#00c');
  })
  .resize();
})
*{ margin:0; padding:0; }

/*
  this is the box which should keep constant the height...
  min-height to allow content to be taller than viewport if too much text
*/
.vhbox{
  min-height:100vh;
  position:relative;
}

.vhbox .t{
  display:table;
  position:relative;
  width:100%;
  height:100vh;
}

.vhbox .c{
  height:100%;
  display:table-cell;
  vertical-align:middle;
  text-align:center;
}
<div class="vhbox" style="background-color:#c00">
  <div class="t"><div class="c">
  this div height should be 100% of viewport and keep this height when scrolling page
    <br>
    <!-- this input highlight if resize event is fired -->
    <input type="text" id="currenth">
  </div></div>
</div>

<div class="vhbox" style="background-color:#0c0">
  <div class="t"><div class="c">
  this div height should be 100% of viewport and keep this height when scrolling page
  </div></div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

采纳答案by nils

Unfortunately this is intentional…

不幸的是,这是故意的……

This is a well know issue (at least in safari mobile), which is intentional, as it prevents other problems. Benjamin Poulain replied to a webkit bug:

这是一个众所周知的问题(至少在 safari mobile 中),这是有意的,因为它可以防止其他问题。Benjamin Poulain 回复了一个 webkit 错误

This is completely intentional. It took quite a bit of work on our part to achieve this effect. :)

The base problem is this: the visible area changes dynamically as you scroll. If we update the CSS viewport height accordingly, we need to update the layout during the scroll. Not only that looks like shit, but doing that at 60 FPS is practically impossible in most pages (60 FPS is the baseline framerate on iOS).

It is hard to show you the “looks like shit” part, but imagine as you scroll, the contents moves and what you want on screen is continuously shifting.

Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.

From the data we had, using the larger view size was the best compromise. Most website using viewport units were looking great most of the time.

这完全是故意的。为了达到这个效果,我们做了很多工作。:)

基本问题是:滚动时可见区域会动态变化。如果我们相应地更新 CSS 视口高度,我们需要在滚动期间更新布局。这不仅看起来很糟糕,而且在大多数页面中以 60 FPS 的速度这样做实际上是不可能的(60 FPS 是 iOS 上的基准帧率)。

很难向您展示“看起来像狗屎”的部分,但是想象一下,当您滚动时,内容会移动,而您想要的屏幕内容也在不断变化。

动态更新高度不起作用,我们有几个选择:在 iOS 上删除视口单位,像 iOS 8 之前一样匹配文档大小,使用小视图大小,使用大视图大小。

从我们拥有的数据来看,使用更大的视图尺寸是最好的折衷方案。大多数使用视口单位的网站在大多数情况下看起来都很棒。

Nicolas Hoizey has researched this quite a bit: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html

Nicolas Hoizey 对此进行了大量研究:https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile -browsers.html

No fix planned

没有修复计划

At this point, there is not much you can do except refrain from using viewport height on mobile devices. Chrome changed to this as well in 2016:

此时,除了避免在移动设备上使用视口高度外,您无能为力。Chrome 在 2016 年也更改为:

回答by Saurabh Jain

You can try min-height: -webkit-fill-available;in your css instead of 100vh. It should be solved

你可以试试min-height: -webkit-fill-available;你的 css 而不是100vh. 应该解决

回答by Andreas Herd

in my app I do it like so (typescript and nested postcss, so change the code accordingly):

在我的应用程序中,我这样做(打字稿和嵌套的 postcss,因此相应地更改代码):

const appHeight = () => {
    const doc = document.documentElement
    doc.style.setProperty('--app-height', `${window.innerHeight}px`)
}
window.addEventListener('resize', appHeight)
appHeight()

in your css:

在你的 css 中:

:root {
   --app-height: 100%;
}

html,
body {
    padding: 0;
    margin: 0;
    overflow: hidden;
    width: 100vw;
    height: 100vh;

    @media not all and (hover:hover) {
        height: var(--app-height);
    }
}

it works at least on chrome mobile and ipad. What doesn't work is when you add your app to homescreen on iOS and change the orientation a few times - somehow the zoom levels mess with the innerHeight value, I might post an update if I find a solution to it.

它至少适用于 chrome mobile 和 ipad。不起作用的是,当您将应用程序添加到 iOS 上的主屏幕并更改几次方向时 - 不知何故缩放级别与 innerHeight 值混淆,如果我找到解决方案,我可能会发布更新。

Demo

演示

回答by Andreas Herd

For many of the sites I build the client will ask for a 100vh banner and just as you have found, it results in a bad "jumpy" experience on mobile when you begin to scroll. This is how I solve the problem for a smooth consistent experience across all devices:

对于我构建的许多网站,客户端会要求 100vh 横幅,正如您发现的那样,当您开始滚动时,它会导致在移动设备上出现糟糕的“跳跃”体验。这就是我解决问题的方法,以便在所有设备上获得流畅一致的体验:

I first set my banner element CSS to height:100vh

我首先将横幅元素 CSS 设置为 height:100vh

Then I use jQuery to get the height in pixels of my banner element and apply an inline style using this height.

然后我使用 jQuery 获取横幅元素的高度(以像素为单位)并使用此高度应用内联样式。

var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });

Doing this solves the issue on mobile devices as when the page loads, the banner element is set to 100vh using CSS and then jQuery overrides this by putting inline CSS on my banner element which stops it from resizing when a user begins to scroll.

这样做解决了移动设备上的问题,因为当页面加载时,横幅元素使用 CSS 设置为 100vh,然后 jQuery 通过将内联 CSS 放在我的横幅元素上来覆盖它,这会在用户开始滚动时阻止它调整大小。

However, on desktop if a user resizes their browser window my banner element won't resize because it now has a fixed height set in pixels due to the above jQuery. To address this I use Mobile Detectto add a 'mobile' class to the body of my document. And then I wrap the above jQuery in an if statement:

但是,在桌面上,如果用户调整浏览器窗口的大小,我的横幅元素将不会调整大小,因为由于上述 jQuery,它现在具有以像素为单位的固定高度设置。为了解决这个问题,我使用Mobile Detect将“移动”类添加到我的文档正文中。然后我将上面的 jQuery 包装在一个 if 语句中:

if ($('body').hasClass('mobile')) {
  var viewportHeight = $('.banner').outerHeight();
  $('.banner').css({ height: viewportHeight });
}

As a result, if a user is on a mobile device the class 'mobile' is present on the body of my page and the above jQuery is executed. So my banner element will only get the inline CSS applied on mobile devices meanwhile on desktop the original 100vh CSS rule remains in place.

因此,如果用户在移动设备上,我的页面正文中会出现“移动”类,并且会执行上述 jQuery。所以我的横幅元素只会在移动设备上应用内联 CSS,而在桌面上,原始 100vh CSS 规则保持不变。

回答by manuel-84

Look at this answer: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

看看这个答案:https: //css-tricks.com/the-trick-to-viewport-units-on-mobile/

// 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`);

// We listen to the resize event
window.addEventListener('resize', () => {
  // We execute the same script as before
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
});
body {
  background-color: #333;
}

.module {
  height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
  margin: 0 auto;
  max-width: 30%;
}

.module__item {
  align-items: center;
  display: flex;
  height: 20%;
  justify-content: center;
}

.module__item:nth-child(odd) {
  background-color: #fff;
  color: #F73859;
}

.module__item:nth-child(even) {
  background-color: #F73859;
  color: #F1D08A;
}
<div class="module">
  <div class="module__item">20%</div>
  <div class="module__item">40%</div>
  <div class="module__item">60%</div>
  <div class="module__item">80%</div>
  <div class="module__item">100%</div>
</div>

回答by Mikhail Vasin

I came up with a React component – check it outif you use React or browse the source codeif you don't, so you can adapt it to your environment.

我想出了一个 React 组件——如果你使用 React,检查它,或者如果你不使用,请浏览源代码,这样你就可以适应你的环境。

It sets the fullscreen div's height to window.innerHeightand then updates it on window resizes.

它将全屏 div 的高度设置为window.innerHeight,然后在调整窗口大小时更新它。

回答by klimat

@nils explained it clearly.

@nils 解释得很清楚。

What's next then?

接下来是什么?

I just went back to use relative 'classic' %(percentage)in CSS.

我刚回去在 CSS 中使用相对的“经典” %(百分比)

It's often more effort to implement something than it would be using vh, but at least, you have a pretty stable solution which works across different devices and browsers without strange UI glitches.

实现某些东西通常比使用它更努力vh,但至少,您有一个非常稳定的解决方案,可以在不同的设备和浏览器上运行,而不会出现奇怪的 UI 故障。

回答by Patryk Janik

For me such trick made a job:

对我来说,这样的伎俩成功了:

height: calc(100vh - calc(100vh - 100%))

回答by Jahaziel

I just found a web app i designed has this issue with iPhones and iPads, and found an article suggesting to solve it using media queries targeted at specific Apple devices.

我刚刚发现我设计的一个网络应用程序在 iPhone 和 iPad 上存在这个问题,并发现一篇文章建议使用针对特定 Apple 设备的媒体查询来解决它。

I don't know whether I can share the code from that article here, but the address is this: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

我不知道我是否可以在这里分享那篇文章中的代码,但地址是这样的:http: //webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Quoting the article: "just match the element height with the device height using media queries that targets the older versions of iPhone and iPad resolution."

引用这篇文章:“只需使用针对旧版 iPhone 和 iPad 分辨率的媒体查询将元素高度与设备高度相匹配。”

They added just 6 media queries to adapt full height elements, and it should work as it is fully CSS implemented.

他们只添加了 6 个媒体查询来适应全高元素,它应该可以正常工作,因为它是完全 CSS 实现的。

Edit pending: I'm unable to test it right now, but I will come back and report my results.

编辑待定:我现在无法测试,但我会回来报告我的结果。

回答by Ray1422

The following code solved the problem (with jQuery).

下面的代码解决了这个问题(使用 jQuery)。

var vhHeight = $("body").height();
var chromeNavbarHeight = vhHeight - window.innerHeight;
$('body').css({ height: window.innerHeight, marginTop: chromeNavbarHeight });

And the other elements use %as a unit to replace vh.

而其他元素%作为一个单元来替换vh