移动端 rem 适配方案

2019-12-15 by 杜宏伟

做pc页面的时候,直接简单的固定页面宽度就可以了。移动端的页面一般都是100%宽,设置的宽都不一样,如何处理?

简介

可以用rem 来做弹性布局。与rem相对的,是vw,不过现在vw的兼容性不大好,毕竟还不能无视 android 4.4 以下版本,象我最近的开发还要兼容到 android 4.0。有的文案是用 vw ,vw不行再回退到 rem。我觉得直接用 rem为好,既然用一种办法就能解决问题,为什么要用两种?

rem 是什么

rem是相对于根元素(html)的字体大小(font-size)来计算的长度单位。 比如 html元素的 font-size100px,那么 1rem就代表100px

js + rem实现弹性布局

首先还得从设计图说起。 设计图一般是以 iphon6 为基准,但是小程序的宽是 750(虽然单位不一样),所以为了保持一致,设计图以750为基准。

有了基准之后,根据当前viewport的宽度来设置html元素的font-size,就实现的弹性的效果。把下面的代码放到head标签里

  function controlRem() {
    var clientWidth = document.documentElement.clientWidth;
    document.getElementsByTagName('html')[0].style.fontSize = clientWidth * / 750 * 100 + 'px'
  }

毕竟是操作了dom元素,所以最安全的作法是把它放在dom建立之后

  window.addEventListener("DOMContentLoaded", controlRem, false)

实际上,这样做是非常必须的,不然的话在某些浏览上会出错,导致页面什么也没有。

上面代码的意义是当viewport750的时候,htmlfont-size设置为100,为什么设置为 100呢,因为这样pxrem的转换就可以直接口算了。比如设计图中的150px,转成rem就是1.5rem

bug处理

由于浏览器或webview众多,所以总会有些有性格的。现象是页面被放大或被缩小,起因都是因为font-size设置“不正确”,这里加引号是因为我们设置的数值是可能是对的,但是因为各种原因,或是缩放,或是浏览器的bug等,导致实际上的font-size不正确。为了解决这个问题,可以在body里播入一个7.5rem宽的div,把它的宽度和viewport做比较,这样就可以做修正了。前面说过,我们是以750为基准的,所以7.5rem就是viewport的宽。

function fix(){
  var SCREEN_WIDTH = window.innerWidth
  var div = document.createElement('div');
  div.style.width = '7.5rem';
  document.body.appendChild(div);
  var getWidth = parseFloat(getComputedStyle(div, false).width);
  if (Math.abs(getWidth - SCREEN_WIDTH)>5) {
      var ratio = getWidth / SCREEN_WIDTH;
      var BASE_FONT_SIZE = parseFloat(document.getElementsByTagName('html')[0].style.fontSize)
      document.getElementsByTagName('html')[0].style.fontSize = (BASE_FONT_SIZE / ratio).toFixed(4) + 'px';
  }
  document.body.removeChild(div)
}

当然了,为了安全起见,执行还是需要在dom建立之后。

max-width , min-width

这两个值是不能用rem的。可以用px 如果要支持min-width,上面的脚本也需要修改,比如要支持最小宽度1000px

  function controlRem() {
    var clientWidth = document.documentElement.clientWidth;
    if (clientWidth < 1000) {
       clientWidth = 1000
    }
    document.getElementsByTagName('html')[0].style.fontSize = clientWidth * / 750 * 100 + 'px'
  }

max-width的处理方式也是一样的。

rempx

px = rem * 100 * clientWidth * / 750

rem 并不万能

比如,对于文字内容较多的场景,文字用px比较合适。

支持横坚的转换

window.addEventListener('orientationchange' in window ? 'orientationchange' : 'resize', controlRem)

一般情况下,是不需要支持横坚转换的。因为如果要支持,需要在UI设计上就需要考虑到,不然效果也不会理想。

浏览器中如何禁止横坚转换?何解?答:无解。凡事有利有弊。相对于webview,这只是众多不利因素的一个。浏览器在设计的时候过分看重使用者的便利,完全忽视了开发者的便利。这样就会失衡,会导致开发者不愿或无法开发在浏览中使用的页面,最后对用户的便利也就变的毫无意义。

1px的问题

1px的问题可以理解成是如何显示最细直线的问题。 1px,2px,及以上都是没问题的。问题就出在高清屏显示1px上面。高清显示是用两个物理单位,所以有人就说,我用一个物理单位显示不是更细吗? 所以这个问题应该是如何用1个物理单位显示的问题。这个更细的线在普通屏上是显示不出来的。不过现在都是高清屏了,所以显示1物理单位的直线也就有变得意义了。 如果那时对高清屏直接把viewport 改成两倍,那么就还是 1css对应1物理显示单位。但是这样会出现另外一个问题:原来的px单位显示都会缩小一半。两害相权,只好是保留原来的显示了。所以唯一的办法就是用2个物理单位显示 1css。 后面相应的样式写法也跟上了,可以用 0.5,但不是所有的浏览器都支持。

现在的情况就是 1px 的视觉效果已经固定的,就是这么宽,与设备无关。但是有的设备有能力显示更细的直线。

个人推荐用伪元素的方式。如果是是一个box,先放大再缩小;如果是一条线,直接缩小。这个方法兼容所有机型。

viewport

最后,也是最重要的,需要在head里加上meta标签。

 <meta name=viewport content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">

起因是先有的pc,后有的移动。pc的宽度大,移动的宽度小。所以移动端的浏览器先自己用一个适合的宽度layoutport(980,或800)来渲染出来,然后再把这个结果进行缩放(一般是缩小)显示在手机屏幕上。默认效果是:如果页面的宽度小于layoutport,可以完全显示出来,不足部分空白补,如果页面的宽度大于layoutport,就只能显示部分。

对于新开发的页面,我可以做的和手机的宽度正好啊,可以用width:100%来实现,所以就不需要浏览器自己处理了。但是因为是先有的pc,所以默认是处理pc页面。后面的移动页面就需要用meta标签告诉浏览器如何处理,不要走默认的处理方式 。

上面的meta告诉浏览器,不需要你自己设定layoutport,直接用设备的显示区域的宽度,这样也就不需要缩放了,多省事。

所以不建议因为 1px 的问题,对viewport去进行缩放,那样会把简单的问题弄复杂。解决1px问题最理想的方案是直接用样式控制,这应该也是未来标准的处理方式。对于样式不支持的情况,用伪元素的方式为好,因为这种方式很容易修改成直接用样式写的方式。

参考

我叫杜宏伟,前端开发。

一直想写博客,在2018的年的最后几天,终于上线了。

对于前端开发,一个特点就是太零散,很容易会了后面忘了前面,所以归纳总结很重要。再有就是分享,做前端好多年,以前都是看你们写的文章, 现在我也开始写一些,希望可以帮到入行的小伙伴。微信号 duhongwei5775

欢迎转载,只需注明作者,出处即可

版权说明:署名 4.0 国际(CC BY 4.0)