了解真实的『REM』手机屏幕适配

rem 作为一个低调的长度单位,由于手机端网页的兴起,在屏幕适配中得到重用。使用 rem 前端开发者可以很方便的在各种屏幕尺寸下,通过等比缩放的方式达到设计图要求的效果。

rem 的官方定义『The font size of the root element.』,即以根节点的字体大小作为基准值进行长度计算。一般认为网页中的根节点是 html 元素,所以采用的方式也是通过设置 html 元素的 font-size 来做屏幕适配,但实际情况真有这么简单吗?

首先我们来看看使用 rem 实现手机屏幕适配的常用方案。

以设计稿的宽度为640px,即:designWidth = 640,同时设定在640px屏宽下 1rem=100px ,即:rem2px = 100。

设置 1rem=100px 的优点不言而喻。前端开发者在切图、重构页面的时候,通过直接位移小数点的方式,就可以将UI图中测量到的 px 值换算成对应的 rem 值,方便快捷。

此外,在 head 中我们还设置了:<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" />

viewport 的作用很重要,但不是本文的重点所以不展开,有兴趣的同学可以自行搜索。

先来看看具体方案:

下面四个方案来自同事共享,原理都是采用等比缩放的方式 —— 获得目标屏幕宽度和设计稿宽度的比,作为 rem 的基值(缩放系数),设置为html标签的字体大小。不同的只是在于性能取舍和书写习惯。

方案1


  1. @media screen and (min-width: 320px) {html{font-size:50px;}} 
  2. @media screen and (min-width: 360px) {html{font-size:56.25px;}} 
  3. @media screen and (min-width: 375px) {html{font-size:58.59375px;}} 
  4. @media screen and (min-width: 400px) {html{font-size:62.5px;}} 
  5. @media screen and (min-width: 414px) {html{font-size:64.6875px;}} 
  6. @media screen and (min-width: 440px) {html{font-size:68.75px;}} 
  7. @media screen and (min-width: 480px) {html{font-size:75px;}} 
  8. @media screen and (min-width: 520px) {html{font-size:81.25px;}} 
  9. @media screen and (min-width: 560px) {html{font-size:87.5px;}} 
  10. @media screen and (min-width: 600px) {html{font-size:93.75px;}} 
  11. @media screen and (min-width: 640px) {html{font-size:100px;}} 
  12. @media screen and (min-width: 680px) {html{font-size:106.25px;}} 
  13. @media screen and (min-width: 720px) {html{font-size:112.5px;}} 
  14. @media screen and (min-width: 760px) {html{font-size:118.75px;}} 
  15. @media screen and (min-width: 800px) {html{font-size:125px;}} 
  16. @media screen and (min-width: 960px) {html{font-size:150px;}}  

方案2


  1. @media screen and (min-width: 320px) {html{font-size:312.5%;}} 
  2. @media screen and (min-width: 360px) {html{font-size:351.5625%;}} 
  3. @media screen and (min-width: 375px) {html{font-size:366.211%;}} 
  4. @media screen and (min-width: 400px) {html{font-size:390.625%;}} 
  5. @media screen and (min-width: 414px) {html{font-size:404.2969%;}} 
  6. @media screen and (min-width: 440px) {html{font-size:429.6875%;}} 
  7. @media screen and (min-width: 480px) {html{font-size:468.75%;}} 
  8. @media screen and (min-width: 520px) {html{font-size:507.8125%;}} 
  9. @media screen and (min-width: 560px) {html{font-size:546.875%;}} 
  10. @media screen and (min-width: 600px) {html{font-size:585.9375%;}} 
  11. @media screen and (min-width: 640px) {html{font-size:625%;}} 
  12. @media screen and (min-width: 680px) {html{font-size:664.0625%;}} 
  13. @media screen and (min-width: 720px) {html{font-size:703.125%;}} 
  14. @media screen and (min-width: 760px) {html{font-size:742.1875%;}} 
  15. @media screen and (min-width: 800px) {html{font-size:781.25%;}} 
  16. @media screen and (min-width: 960px) {html{font-size:937.5%;}}  

方案3


  1. var designWidth = 640, rem2px = 100; 
  2. document.documentElement.style.fontSize =  
  3.   ((window.innerWidth / designWidth) * rem2px) + 'px';  

方案4


  1. var designWidth = 640, rem2px = 100; 
  2. document.documentElement.style.fontSize =  
  3.   ((((window.innerWidth / designWidth) * rem2px) / 16) * 100) + '%';  

为了更避免理解上的混乱,我在上面js的代码中加了 ( ) ,实际代码中是不需要的。

详细分析一下,rem 和 px 直接的转换公式可以写为:


  1. 1rem = 1 * htmlFontSize 

htmlFontSize 为 html 元素的字体大小。

首先来看方案1中,在屏宽为640px情况下的设置:


  1. @media screen and (min-width: 640px) {html{font-size:100px;}} 

可以很明显的表现出这一点 1rem = 1 * 100px ,同我们最初的设定。那么我们要得到其它屏幕大小的 htmlFontSize 值要怎么办。很简单如方案3,因为我们的采用等比缩放的方式适配,所以计算目标屏幕宽度和设计稿的宽度的比即可:


  1. window.innerWidth / designWidth * rem2px + 'px' 

由于浏览器默认字体大小为 16px,所以当我们使用百分比作为根节点 html 的字体大小时,即html元素的font-size值设置为一个百分比值,rem 的计算方式就会改为:


  1. defaultFontSize = 16px 
  2. 1rem = 1 * htmlFontSize * defaultFontSize  

如方案2中,在屏宽为640px情况下的设置:


  1. @media screen and (min-width: 640px) {html{font-size:625%;}} 

应用上面的公式:


  1. 1rem = 1 * 625% * 16px  

其中:625% * 16 = 6.25 * 16 = 100

所以:1rem = 1 * 100px

同样的可以得到所有屏幕大小下,html 的 font-size 值的计算公式,即为方案4:


  1. window.innerWidth / designWidth * rem2px / 16 * 100 + '%' 

通过方案3和方案4的公式,就可以很方便的生成方案1和方案2中的css。

这里只给出了方案3和方案4对应验证页面(方案1和方案2是它们的变形): scheme3.html(http://htmlpreview.github.io/?https://github.com/hbxeagle/rem/blob/master/scheme3.html), scheme4.html(http://htmlpreview.github.io/?https://github.com/hbxeagle/rem/blob/master/scheme4.html)

如下面两张图,是在屏宽为360px下的效果,通过计算目标为:1rem = 56.25px。方案3设置值为:56.25px,方案4设置值为:351.5625% 

到目前为止貌似很完美的解决了问题,实际情况当然是出现了意外。在有些 Android 手机上,浏览器或 webview 的默认字体是随着系统设置的字体改变的。这样就会导致默认字体大于或小于 16px。

修改默认字体大小后,我们再看方案3和方案4。

同样在屏宽为360px下,我们调大系统字体大小,如下面的效果

设置前 html 元素的字体大小的计算值为 18px ,设置后的计算值为 65px ,由于屏幕宽度没有改变,我们的目标值,即我们在 html 元素上设置的 font-size 值也没有变化任然为 56.25px,而最终计算值出现了偏差。 

分析偏差前,先来看在360px屏宽下,方案3和方案4的计算过程:

方案3:


  1. document.documentElement.style.fontSize = 56.25px 
  2. htmlFontSize = 56.25px 
  3. 1rem = 1 * htmlFontSize = 56.25px  

实际为:

1rem = 64.6875px

方案4:


  1. document.documentElement.style.fontSize = 351.5625% 
  2. htmlFontSize = 351.5625% 
  3. defaultFontSize = 18px 
  4. 1rem = 1 * htmlFontSize * defaultFontSize = 351.5625% * 18px = 63.28125px 
  5. 351.5625% * 18 = 63.28125 

实际为:

1rem = 64.6875px

貌似方案4的计算结果很接近实际效果,而方案3偏差很大。再来比较方案3和方案4的计算公式:


  1. // 方案3 
  2. document.documentElement.style.fontSize =  
  3.   window.innerWidth / designWidth * rem2px + 'px'; 
  4.   
  5. // 方案4 
  6. document.documentElement.style.fontSize =  
  7.   window.innerWidth / designWidth * rem2px / 16 * 100 + '%';  

方案4较于方案3其实多除了一个16,可以推测浏览器在计算 rem 的具体值时,如果 html 设置的 font-size 为 px 值时会先除以 16 ,然后再乘以 htmlFontSize。


  1. 1rem = 1 * (56.25px / 16) * 18 
  2. 1 * (56.25 / 16) * 18 = 63.28125  

方案4存在问题,是因为系统的默认字体改为了 18px ,但是我们在计算百分比是时候,还是以 16px 为基准值进行计算,所以出现偏差(计算值和实际值之间还有一点偏差这个在后面会提到)。

而在方案3中,我们其实是不考虑浏览器默认字体大小的,但在实际使用的过程中,浏览器还是除了 16 ,而此时默认字体大小为 18px。得出如下在 html 的 fontSize 设置为 px 的情况下 rem 的计算公式为:


  1. 1rem = 1 * (htmlFontSize / 16) * defaultFontSize 

在系统设置的字体大小发生改变时,defaultFontSize 会跟着改变,而 16 不会变化。所以方案3虽然表面上不考虑默认字体大小的变化,只关注屏幕与设计稿之间的宽度比,但在实际计算中还是使用到了默认字体大小,而且还有一个不变的 16 在作祟,导致方案3失败。

所谓的「root element」其实不是想象的那样,一个是16,一个是18,到底取的是那个 root element 的字体大小。

ok,rem 的计算的时候,px 的方式会有一个16不随系统字体大小改变,所以我们采用百分比的方案,绕开这个问题。

采用百分比的方案4因为在计算时写死了默认字体大小 16px。所以它的偏差在于没能动态的获取默认字体大小。更新如下:

方案4.1


  1. var designWidth = 640, rem2px = 100; 
  2. var h = document.getElementsByTagName('html')[0]; 
  3. var htmlFontSize = parseFloat(window.getComputedStyle(h, null) 
  4.                                     .getPropertyValue('font-size')); 
  5.   
  6. document.documentElement.style.fontSize =  
  7.   window.innerWidth / designWidth * rem2px / htmlFontSize * 100 + '%';  

效果如下图:

16px 的图中,设置后的 html 的 font-size 与 1rem 的实际值有偏差,同时 6.4rem 的计算值也有偏差。通过查看代码发现html的font-size使用的是: getPropertyValue('font-size') 而 1rem 使用的是getPropertyValue('width'),偏差出在计算 font-size 的时候浏览器进行了四舍五入。rem 定义中的另一个元素「font size」也不能按字面意思使用,宣告失守。

18px 中的偏差,以及上文中方案4在 18px 实际值和计算值出现的偏差都是同样的问题。所以基准值还需要修改。

在更新一版,方案4.2:


  1. var designWidth = 640, rem2px = 100; 
  2. var d = window.document.createElement('div'); 
  3. d.style.width = '1rem'; 
  4. d.style.display = "none"; 
  5. var head = window.document.getElementsByTagName('head')[0]; 
  6. head.appendChild(d); 
  7. var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width')); 
  8. d.remove(); 
  9. document.documentElement.style.fontSize =  
  10.   window.innerWidth / designWidth * rem2px / defaultFontSize * 100 + '%';  

效果如下图:

到此为止,rem 在默认字体不是 16px 的情况下的处理已经解决,考虑到还有设计屏幕旋转,最终手机端的解决方案为:


  1. function adapt(designWidth, rem2px){ 
  2.   var d = window.document.createElement('div'); 
  3.   d.style.width = '1rem'; 
  4.   d.style.display = "none"; 
  5.   var head = window.document.getElementsByTagName('head')[0]; 
  6.   head.appendChild(d); 
  7.   var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width')); 
  8.   d.remove(); 
  9.   document.documentElement.style.fontSize = window.innerWidth / designWidth * rem2px / defaultFontSize * 100 + '%'; 
  10.   var st = document.createElement('style'); 
  11.   var portrait = "@media screen and (min-width: "+window.innerWidth+"px) {html{font-size:"+ ((window.innerWidth/(designWidth/rem2px)/defaultFontSize)*100) +"%;}}"; 
  12.   var landscape = "@media screen and (min-width: "+window.innerHeight+"px) {html{font-size:"+ ((window.innerHeight/(designWidth/rem2px)/defaultFontSize)*100) +"%;}}" 
  13.   st.innerHTML = portrait + landscape; 
  14.   head.appendChild(st); 
  15.   return defaultFontSize 
  16. }; 
  17. var defaultFontSize = adapt(640, 100);  

回过头来再看 rem 的定义,『The font size of the root element.』。我们以为的 root element —— html 其实还有个影子在作祟,而我们以为的 font-size 其实是个近似值。

本文作者:佚名

来源:51CTO

时间: 2024-12-22 19:45:16

了解真实的『REM』手机屏幕适配的相关文章

Android 手机屏幕适配解决办法_Android

0. 前言 Android的屏幕适配,即使得某一元素在Android不同尺寸.不同分辨率的手机上具备相同的显示效果,这个问题一直以来都是我们Android开发者不得不面对的问题.本文参考了很多前人的博客,并对这一问题做一个总结,力求精简明了. 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52690498 1. 基础概念 (1)屏幕尺寸,即手机对角线的物理尺寸 1英寸 = 2.54cm  常见手机尺寸有5英寸.5.5英寸.6英寸等

手机屏幕适配

随着手机屏幕的不断的增大,同时也遇到一些用户手机屏幕还是处于240*320这种屏幕的大小,当然也存着在一些不规则的http://www.aliyun.com/zixun/aggregation/10134.html">屏幕分辨率心寸大小.对于很多的UI来说,不同的手机屏幕很多时候得出多套的图才能保证手机客户端在不同的屏幕上实现匹配.针对手机客户端在不同屏幕下的实现进行规划,并制定出能够计算出具体控件位置的填充区算法,这个是前期对于不同的手机屏幕的匹配做出的一个界面适配算法. 首先认清几个手

Android 手机屏幕适配解决办法

0. 前言 Android的屏幕适配,即使得某一元素在Android不同尺寸.不同分辨率的手机上具备相同的显示效果,这个问题一直以来都是我们Android开发者不得不面对的问题.本文参考了很多前人的博客,并对这一问题做一个总结,力求精简明了. 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52690498 1. 基础概念 (1)屏幕尺寸,即手机对角线的物理尺寸 1英寸 = 2.54cm  常见手机尺寸有5英寸.5.5英寸.6英寸等

android手机虚拟导航栏屏幕适配

问题描述 android手机虚拟导航栏屏幕适配 两款手机都是1080*1920的分辨率,一个手机又导航栏,一个手机没有,请问怎么适配 解决方案 http://www.cocoachina.com/bbs/read.php?tid-224081.htmlhttp://www.cnblogs.com/error404/p/3815739.html 解决方案二: 如果是适配这两个手机,你就用dp够了,然后多用match_parent和weight 解决方案三: android 手机屏幕适配Androi

Android编程实现获得手机屏幕真实宽高的方法_Android

本文实例讲述了Android编程实现获得手机屏幕真实宽高的方法.分享给大家供大家参考,具体如下: WindowManager w = activity.getWindowManager(); Display d = w.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); d.getMetrics(metrics); // since SDK_INT = 1; widthPixels = metrics.width

[译] 『小键盘』难题:用户在手机上填写表单吗?

本文讲的是[译] 『小键盘』难题:用户在手机上填写表单吗?, 原文地址:The Tiny Keyboard Problem: Do People Complete Forms on Their Phones? 原文作者:Priceonomics Data Studio 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:Changkun Ou 校对者:ylq167 『小键盘』难题:用户在手机上填写表单吗? 本文系根据 JotForm (一家 Priceon

HTML5 移动页面自适应手机屏幕宽度详解_相关技巧

网上关于这方面的文章有很多,重复的东西本文不再赘述,仅提供思路,并解释一些其他文章讲述模糊的地方.  1.使用meta标签,这也是普遍使用的方法,理论上讲使用这个标签是可以适应所有尺寸的屏幕的,但是各设备对该标签的解释方式及支持程度不同造成了不能兼容所有浏览器或系统. 首先解释该标签的含义: <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, ma

手机屏幕尺寸扩展是如何影响用户体验设计的

  造型千奇百怪的小屏手机叱咤风云的时代已经一去不复返了.事实上,近几年的行业趋势表明大屏手机,或者说巨屏手机,将会在很长的时间内占据主流.而现在,也是时候总结一下过去几年里,面对大屏手机时,设计师的失职. 如何界定大屏手机? 其实简单称之为大屏也不是特别准确,它的英文名称更为形象:Phone+Tablet=Phablet ,也就是说,它是传统手机和平板的结合体,Phablet. 因此,这些大屏手机实际上是超过我们手掌习惯的.可掌控的尺寸,但是又没有达到平板的级别.更准确的说,是屏幕尺寸在5~6

『 Spark 』5. 这些年,你不能错过的 spark 学习资源

原文链接:『 Spark 』5. 这些年,你不能错过的 spark 学习资源 写在前面 本系列是综合了自己在学习spark过程中的理解记录 + 对参考文章中的一些理解 + 个人实践spark过程中的一些心得而来.写这样一个系列仅仅是为了梳理个人学习spark的笔记记录,所以一切以能够理解为主,没有必要的细节就不会记录了,而且文中有时候会出现英文原版文档,只要不影响理解,都不翻译了.若想深入了解,最好阅读参考文章和官方文档. 其次,本系列是基于目前最新的 spark 1.6.0 系列开始的,spa