iOS 手机元素样式依赖 vh,滚动时页面高度变化导致页面展示异常

问题:iOS 手机 safari 浏览器打开页面,又或微信内打开页面时,在页面滚动时,若有元素的样式是根据 vh 进行设置时,由于底部的状态栏的出现与隐藏,会导致 vh 发生变化,从而导致页面布局出现错乱;

解决方法:

a. 修改样式,vh改为固定高度或通过 vw, rem 等进行设置;缺点是部分展示效果无法完美通过此种方式实现;不推荐,要求不高可用;

b. 在页面加载时,通过style 标签提前设置,将对应的样式提前转化为 px 单位,并设置为 !important,避免后面滚动过程中页面布局会出现错乱;完美解决所有问题,推荐。

b 的解决原理是通过在页面的 head 标签中,通过 script 标签执行js ,动态生成 style ,将样式中存在 vh 有关的元素的样式,提前转化为 px!import。主要的实现,重点在于两个逻辑,vh 转化 px 与动态生成 style 标签。

vh 转化为 px:

function vhToPx (val) {
  const h = Math.max(document.documentElement.clientHeight,window.innerHeight);
  return parseFloat(h * val).toFixed(2); // 设置 px 时保留到小数点后两位
}

动态生成并写入 style

const cssText = '';
cssText += '.className {height:' + vhToPx(0.477216) + 'px!important;}.className {width: ' + vhToPx(0.357142) + 'px!important;height: ' + vhToPx(0.477216) + 'px!important;}';
const cssBlock = document.createElement('style');
cssBlock.innerHTML=cssText;
document.head.insertBefore(cssBlock, document.head.childNodes[document.head.childNodes.length - 1].nextSibling);

使用:

<script
	//获取视口高度设置元素的固定宽高,避免 iOS 手机页面滚动高度变化时样式会乱
  dangerouslySetInnerHTML={{
  	__html: `function vhToPx (val) {var h = Math.max(document.documentElement.clientHeight,window.innerHeight);return parseFloat(h * val).toFixed(2);}var cssText = '';cssText += '.className{height:' + vhToPx(0.477216) + 'px!important;}.className {width: ' + vhToPx(0.357142) + 'px!important;height: ' + vhToPx(0.477216) + 'px!important;}';var cssBlock = document.createElement('style');cssBlock.innerHTML=cssText;document.head.insertBefore(cssBlock, document.head.childNodes[document.head.childNodes.length - 1].nextSibling);`,
  }}
/>

同理,该方法也可解决 hybrid 应用的 webview 设置通顶的问题。 hybrid 应用的 webview 设置通顶时,有些应用会使整个 title 透明,这个时候就需要我们给原本的 title 位置添加个元素进行占位,同时,页面元素如果有做吸顶等效果,也需要添加 title 对应高度的偏移。为了适应不同机型,我们可以可以用上面同样的方式,在 head 中设置上面说的这些元素的样式。

Q.E.D.


Take it easy