热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

@media移动端屏幕适配

rem作为一个低调的长度单位,由于手机端网页的兴起,在屏幕适配中得到重用。使用rem前端开发者可以很方便的在各种屏幕尺寸下,通过等比缩放的
rem 作为一个低调的长度单位,由于手机端网页的兴起,在屏幕适配中得到重用。使用 rem 前端开发者可以很方便的在各种屏幕尺寸下,通过等比缩放的方式达到设计图要求的效果。(当然了,他是一个相对单位,以html中的font-size为基准,这也是优于以em为单位的好处)




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 中我们还设置了: 

viewport 的作用很重要,(不加这个meta标签,我不会告诉你在手机屏幕上字体显示特别小)但不是本文的重点所以不展开,有兴趣的同学可以自行搜索。

下面四个方案来自同事共享,原理都是采用等比缩放的方式 —— 获得目标屏幕宽度和设计稿宽度的比,作为 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, scheme4.html

如下面两张图,是在屏宽为360px下的效果,通过计算目标为:1rem = 56.25px。方案3设置值为:56.25px,方案4设置值为:351.5625%
方案3        方案4
        
到目前为止貌似很完美的解决了问题,实际情况当然是出现了意外。在有些 Android 手机上,浏览器或 webview 的默认字体是随着系统设置的字体改变的。这样就会导致默认字体大于或小于 16px。

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

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

设置前 html 元素的字体大小的计算值为 18px ,设置后的计算值为 65px ,由于屏幕宽度没有改变,我们的目标值,即我们在 html 元素上设置的 font-size 值也没有变化任然为 56.25px,而最终计算值出现了偏差。
方案3        方案4
        
分析偏差前,先来看在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 实际值和计算值出现的偏差都是同样的问题。所以基准值还需要修改。
16px        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 + '%';
效果如下图:

16px        18px
        
到此为止,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
};

方案五,当然了,如果你会使用less或者sass,利用less就会自动计算出不同屏幕下的字体大小,样式会跟方案一相同,下面贴代码:

.re(@width){
    @xs : 100px/(640px/@width);
    @media (max-width:(@width + 2px)){
        html{
            font-size:@xs;
        }
    }
}

你只需要写入屏幕大小即可

.re(320px);

.re(768px);
.re(640px);
.re(600px);


推荐阅读
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 本文将介绍如何在混合开发(Hybrid)应用中实现Native与HTML5的交互,包括基本概念、学习目标以及具体的实现步骤。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 第二十五天接口、多态
    1.java是面向对象的语言。设计模式:接口接口类是从java里衍生出来的,不是python原生支持的主要用于继承里多继承抽象类是python原生支持的主要用于继承里的单继承但是接 ... [详细]
  • Cookie学习小结
    Cookie学习小结 ... [详细]
  • 本文介绍了如何在 ASP.NET 中设置 Excel 单元格格式为文本,获取多个单元格区域并作为表头,以及进行单元格合并、赋值、格式设置等操作。 ... [详细]
  • LDAP服务器配置与管理
    本文介绍如何通过安装和配置SSSD服务来统一管理用户账户信息,并实现其他系统的登录调用。通过图形化交互界面配置LDAP服务器,确保用户账户信息的集中管理和安全访问。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 本文介绍了 AngularJS 中的 $compile 服务及其用法,通过示例代码展示了如何使用 $compile 动态编译和链接 HTML 元素。 ... [详细]
  • 自动验证时页面显示问题的解决方法
    在使用自动验证功能时,页面未能正确显示错误信息。通过使用 `dump($info->getError())` 可以帮助诊断和解决问题。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 微软推出Windows Terminal Preview v0.10
    微软近期发布了Windows Terminal Preview v0.10,用户可以在微软商店或GitHub上获取这一更新。该版本在2月份发布的v0.9基础上,新增了鼠标输入和复制Pane等功能。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
  • 两个条件,组合控制#if($query_string~*modviewthread&t(&extra(.*)))?$)#{#set$itid$1;#rewrite^ ... [详细]
author-avatar
诺亚方舟菲芘袁子陽
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有