了解真实的『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

@media screen and (min-width: 320px) {html{font-size:50px;}}

@media screen and (min-width: 360px) {html{font-size:56.25px;}}

@media screen and (min-width: 375px) {html{font-size:58.59375px;}}

@media screen and (min-width: 400px) {html{font-size:62.5px;}}

@media screen and (min-width: 414px) {html{font-size:64.6875px;}}

@media screen and (min-width: 440px) {html{font-size:68.75px;}}

@media screen and (min-width: 480px) {html{font-size:75px;}}

@media screen and (min-width: 520px) {html{font-size:81.25px;}}

@media screen and (min-width: 560px) {html{font-size:87.5px;}}

@media screen and (min-width: 600px) {html{font-size:93.75px;}}

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

@media screen and (min-width: 680px) {html{font-size:106.25px;}}

@media screen and (min-width: 720px) {html{font-size:112.5px;}}

@media screen and (min-width: 760px) {html{font-size:118.75px;}}

@media screen and (min-width: 800px) {html{font-size:125px;}}

@media screen and (min-width: 960px) {html{font-size:150px;}}

方案2

@media screen and (min-width: 320px) {html{font-size:312.5%;}}

@media screen and (min-width: 360px) {html{font-size:351.5625%;}}

@media screen and (min-width: 375px) {html{font-size:366.211%;}}

@media screen and (min-width: 400px) {html{font-size:390.625%;}}

@media screen and (min-width: 414px) {html{font-size:404.2969%;}}

@media screen and (min-width: 440px) {html{font-size:429.6875%;}}

@media screen and (min-width: 480px) {html{font-size:468.75%;}}

@media screen and (min-width: 520px) {html{font-size:507.8125%;}}

@media screen and (min-width: 560px) {html{font-size:546.875%;}}

@media screen and (min-width: 600px) {html{font-size:585.9375%;}}

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

@media screen and (min-width: 680px) {html{font-size:664.0625%;}}

@media screen and (min-width: 720px) {html{font-size:703.125%;}}

@media screen and (min-width: 760px) {html{font-size:742.1875%;}}

@media screen and (min-width: 800px) {html{font-size:781.25%;}}

@media screen and (min-width: 960px) {html{font-size:937.5%;}}

方案3

var designWidth = 640, rem2px = 100;

document.documentElement.style.fontSize =

((window.innerWidth / designWidth) * rem2px) + ‘px‘;

方案4

var designWidth = 640, rem2px = 100;

document.documentElement.style.fontSize =

((((window.innerWidth / designWidth) * rem2px) / 16) * 100) + ‘%‘;

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

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

1rem = 1 * htmlFontSize

htmlFontSize 为 html 元素的字体大小。

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

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

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

window.innerWidth / designWidth * rem2px + ‘px‘

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

defaultFontSize = 16px

1rem = 1 * htmlFontSize * defaultFontSize

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

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

应用上面的公式:

1rem = 1 * 625% * 16px

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

所以:1rem = 1 * 100px

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

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:

document.documentElement.style.fontSize = 56.25px

htmlFontSize = 56.25px

1rem = 1 * htmlFontSize = 56.25px

实际为:

1rem = 64.6875px

方案4:

document.documentElement.style.fontSize = 351.5625%

htmlFontSize = 351.5625%

defaultFontSize = 18px

1rem = 1 * htmlFontSize * defaultFontSize = 351.5625% * 18px = 63.28125px

351.5625% * 18 = 63.28125

实际为:

1rem = 64.6875px

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

// 方案3

document.documentElement.style.fontSize =

window.innerWidth / designWidth * rem2px + ‘px‘;

// 方案4

document.documentElement.style.fontSize =

window.innerWidth / designWidth * rem2px / 16 * 100 + ‘%‘;

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

1rem = 1 * (56.25px / 16) * 18

1 * (56.25 / 16) * 18 = 63.28125

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

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

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

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

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

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

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

方案4.1

var designWidth = 640, rem2px = 100;

var h = document.getElementsByTagName(‘html‘)[0];

var htmlFontSize = parseFloat(window.getComputedStyle(h, null)

.getPropertyValue(‘font-size‘));

document.documentElement.style.fontSize =

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:

var designWidth = 640, rem2px = 100;

var d = window.document.createElement(‘div‘);

d.style.width = ‘1rem‘;

d.style.display = "none";

var head = window.document.getElementsByTagName(‘head‘)[0];

head.appendChild(d);

var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue(‘width‘));

d.remove();

document.documentElement.style.fontSize =

window.innerWidth / designWidth * rem2px / defaultFontSize * 100 + ‘%‘;

效果如下图:

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

function adapt(designWidth, rem2px){

var d = window.document.createElement(‘div‘);

d.style.width = ‘1rem‘;

d.style.display = "none";

var head = window.document.getElementsByTagName(‘head‘)[0];

head.appendChild(d);

var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue(‘width‘));

d.remove();

document.documentElement.style.fontSize = window.innerWidth / designWidth * rem2px / defaultFontSize * 100 + ‘%‘;

var st = document.createElement(‘style‘);

var portrait = "@media screen and (min-width: "+window.innerWidth+"px) {html{font-size:"+((window.innerWidth/(designWidth/rem2px)/defaultFontSize)*100) +"%;}}";

var landscape = "@media screen and (min-width: "+window.innerHeight+"px) {html{font-size:"+((window.innerHeight/(designWidth/rem2px)/defaultFontSize)*100) +"%;}}"

st.innerHTML = portrait + landscape;

head.appendChild(st);

return defaultFontSize

};

var defaultFontSize = adapt(640, 100);

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

时间: 2024-08-06 03:17:09

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

了解真实的『REM』多终端屏幕适配

作者:hbxeagle 链接:github.com/hbxeagle/rem/blob/master/README.md rem 作为一个低调的长度单位,由于手机端网页的兴起,在屏幕适配中得到重用.使用 rem 前端开发者可以很方便的在各种屏幕尺寸下,通过等比缩放的方式达到设计图要求的效果. rem 的官方定义『The font size of the root element.』,即以根节点的字体大小作为基准值进行长度计算.一般认为网页中的根节点是 html 元素,所以采用的方式也是通过设置

【转】web app变革之rem(手机屏幕实现全适配)

以往web移动适配,常规写法是:media only screen @media only screen and (min-device-width: 320px){ //针对iPhone 3 } @media only screen and (min-device-width: 320px)and (-webkit-min-device-pixel-ratio: 2) { //针对iPhone 4, 5c,5s, 所有iPhone6的放大模式,个别iPhone6的标准模式 } @media o

手机屏幕适配原理及实现

手机屏幕是用户与 App 最直接的交互点 不同的分辨率下用户对我们的 App 具有明显的感观差异,主流分辨率的更新迭代却又完全独立于 App 进行.这让我们想要使 App 在绝大多数主流手机上都保持感观.体验的一致性提出了很大的挑战. 屏幕尺寸 严格来说,屏幕尺寸实际被物理尺寸和显示分辨率两个部分定义. 而我们今天对各类手机.Pad 设备所说的屏幕尺寸,只指物理尺寸. 如果一块手机屏幕的物理尺寸是 5.5 英寸,即是指该手机屏幕对角线的长度. 屏幕分辨率 屏幕分辨率是指屏幕图像的精密度,是显示器

关于直播系统和短视频系统安卓手机屏幕适配的方向思路

直播与短视频的接踵而至,将互联网推向了网红经济时代,而智能手机的更新迭代为二者的发展提供了"温床".但随着Android机型的增多,设备碎片化的程度也在不断加深,因此为了保证用户在不同Android机型下的体验效果一致,我们需要对各种手机屏幕进行适配.下面我们就来聊下直播系统和短视频系统Android机型的适配问题.1. 布局组件的适配关于组件的适配,这里有几种不同的适配方案.一是使用密度无关像素dp或独立比例像素sp单位指定尺寸:二是多使用相对布局(RelativeLayout)或线

Unity 手机屏幕适配

////如有侵权 请联系我进行删除 email:[email protected] 1.游戏屏幕适配 屏幕适配是为了让我们的项目能够跑在各种电子设备上(手机,平板,电脑) 那么了解是适配之前首先要了解两个知识点: 1-1.什么是像素? 单位面积中构成图像的点的个数. 特点:单位面积内的像素越多,分辨率越高,图像的效果就越好. 1-2.什么是分辨率? 分辨率可以从显示分辨率与图像分辨率两个方向来分类. 示分辨率(屏幕分辨率)是屏幕图像的精密度,是指显示器所能显示的像素有多少.分辨率的单位有:(dp

了解真实的 rem 手机屏幕适配

http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651553894&idx=2&sn=264373bdb819fe28052639f99bdbf10d&chksm=802557a7b752deb18d729cd8dd9a1d38bc114945a92bce0f1abe1ec5edbc897b1b897476bfc5&mpshare=1&scene=1&srcid=040396v4sxxIZB

android手机屏幕适配方法

1.你需要在AndroidManifest.xml文件的元素如下添加子元素    android:normalScreens="true" android:anyDensity="true"    android:smallScreens="true">    名如其意,以上是为我们的屏幕设置多分辨率支持(更准确的说是适配大.中.小三种密度).android:anyDensity="true" ,这一句对整个的屏幕都起着

手机屏幕适配遇到虚拟键的问题

============问题描述============ 最近做手机适配,适配了800*480的手机,可是批量测试的时候发现有几款手机也是800*480的,但是界面元素错位,经查发现是屏幕确实是800*480的,但是有虚拟键,占了几十的像素.通过程序取出来的分辨率是782*480的.像这样的情况用不用再对这款782*480的进行适配,或者有没有什么好的办法解决这个问题.因为可能面临的手机客户很多,也不知道有没有虚拟键. 请高手发表观点.谢谢啦 ============解决方案1=========

CSS3自适配手机屏幕

@media only screen and (max-width:350px){ .img{ width: 80px; height:70px; background-image: url(./images/heart2.png); background-size: 100% 100%; float: left; margin-top: 5%; margin-left: 5%; font-size: 10px; } } @media only screen and (min-width:350