移动端开发不可避免的一个点就是适配问题,由于机型繁多,屏幕尺寸大不相同,以及retina屏幕导致一套固定的样式布局无法满足所有的屏幕需求,因此需要解决各种屏幕的适配问题,让页面在所有手机上看起来都合适美观。
到目前为止,在适配问题上经历了几个阶段:
- 百分比布局,抛弃px尽量用%,对于一些较小的元素和边框来说并不友好,个人认为%比较适合做页面结构划分,不太适合较小元素的大小设置
- 媒体查询(@media),根据屏幕宽度执行不同样式,但是只能按设备宽度区间做大体区分,不够细致,代码繁重
- 百分比+媒体查询,通过百分比做layout布局,具体元素用媒体查询单独设置,缺点同上
- rem布局,通过rem单位,动态设置html标签的fontSize,目前比较成熟的适配方案,常用的有淘宝适配方案的flexible,蚂蚁金服的ant design mobile
- flex布局,移动端兼容较好
后面会针对rem适配具体说明
基本概念
物理像素
又叫设备像素,一个物理像素是屏幕上的最小显示单元,每个像素都有自己的显示颜色和亮度,例如,屏幕上显示的一张100px*100px的图片,实际上是由很多个拥有颜色和亮度的像素点组成。
设备独立像素
又叫做密度无关像素、逻辑像素,是由程序可以控制的虚拟像素,我们样式中的css像素也是逻辑像素的一种。系统会将逻辑像素转化为设备像素展示。
设备像素比
物理像素与设备像素之间的关系就是设备像素比,dpr = 物理像素/设备独立像素。
可以通过 window.devicePixelRatio 获取设备像素比。
即使知道了基本概念,可能也还是不清楚他们之间到底有什么关系?有怎样的表现?以下通过简单的例子来进行理解。
上图是屏幕尺寸相同,dpr分别为1和2的展示效果,最直观的视觉效果就是左侧dpr=1的显示比较模糊粗糙,右侧dpr=2的更加清晰细致,产生区别的原因不是因为右侧的图案比左侧的大,而是在尺寸相同的情况下右侧屏幕为retina屏幕,dpr=2,这意味着,当展示一个100px*100px大小的图案时,dpr=1的屏幕上在100px*100px的区域内显示了100*100个物理像素点,用一个物理像素显示1个逻辑(css)像素,但是在dpr=2的屏幕上却展示了100*100*4个像素点,用4个物理像素展示1个逻辑(css)像素,所以图案更加清晰细腻。如下图所示:
也可以简单的理解为,在尺寸相同,密度不同的屏幕上展示同等大小的内容,那么密度大的就要用更多的像素去展示。这里的密度指单位面积内的像素点个数,像素点越多,分辨率越大,越清晰。
主流适配方案
目前流行的移动端适配方案分别为蚂蚁金服的ant-design-mobile中的高清适配方案和淘宝的flexible,但是两种方案的原理是一样的,viewport+rem,这里主要记录两种方案的使用方式,原理在下面分析。
ant-design
阿里蚂蚁金服的react组件库,在antd mobile中提供移动端高清适配方案,简单介绍下使用使用方式,在创建项目并安装antd-mobile后
1.安装依赖
npm install --save-dev babel-plugin-import less less-loader
2.修改webpack配置
修改配置支持按需引入,加载antd-mobile的less文件
增加配置,解析编译less,引入主题配置,这里的theme是package.json中的配置项
package.json中增加theme设置项,theme可以定制组件皮肤颜色,高清方案hd需为2px,具体参数参看antd-mobile官网,这里也有使用方式
3.px转rem
antd里px转rem方便快捷的方式是通过依赖postcss-plugin-px2rem,配置后在开发中可以直接写px单位,编译后生成px单位。
安装依赖:
npm install postcss-plugin-px2rem postcss-loader --save-dev
对.css文件做解析编译配置,增加postcss-loader配置,postcss-loader的options里增加postcss-plugin-px2rem相关配置,如下图,具体使用根据自己工程配置方式做变通:
rootValue默认设置100,即1rem = 100px;
flexible
1.引入flexible.js文件
flexible.js可以下载到本地工程引入,也可以引cdn上的远程文件,该文件执行会自动生成 标签,不需要手动写
2.开发使用
由于会自动生成meta标签,并自动设置html标签的font-size属性,所以只需要按照设计稿写rem单位的css即可。默认iphone6 1rem = 75px,其计算方式是1rem=document.documentElement.clientWidth*dpr/100 = 375*2/100=75px
无轮以上那种方式,在开发模式上可以用同一种,以iphone6为标准,UI出750*1334(高度不固定)的设计稿,前端开发元素大小同设计图。
px转rem方式
px转rem有几种比较好用的方式
1、不用sass,less,直接在写css的过程中自行计算rem,麻烦耗时并且开工作量大,如果以后改变rem对px的开发标准,那么全部需要重新计算。
2、 使用sass开发,且不用构建工具,那么可以使用sass的混合宏、函数计算rem;也可以IDE安装转换插件。sass函数如下:
// px rem转换函数
@function px2rem($px, $base-font-size: 75px) {
@if (unitless($px)) {
@warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
@return px2rem($px + 0px);
} @else if (unit($px) == rem) {
@return $px;
}
@return ($px / $base-font-size) * 1rem;
}
3、 使用构建工具,可以npm安装px2rem插件
antd推荐:https://github.com/ant-tool/p...
flexible推荐:https://github.com/songsiqi/p...
适配原理解析
移动端适配的根本在于rem+viewport
css单位由px转向rem
这里暂时不考虑retina屏幕问题,下面解析
这里说一下px、em、rem单位的区别:
1px = 1px
1em = 父元素的font-size
1rem = html标签的font-size
由于rem单位的特性,使得以rem作为单位的元素在大小等属性上具有相对性,以元素的width,height为例,根元素的font-size越大,那么元素的宽高越大,反之越小。这就意味着当我们以iphone6为基础做rem布局:
iphone6
html: font-size = 100px
div: width = 1rem = 100px
height = 1rem = 100px
在 iphone6 plus 上的显示会如下,由于plus屏幕较大,html的fontSize属性等比例变大,元素尺寸同样会相应变大,这样就可以达到根据屏幕尺寸不同,页面元素做等比例缩放,达到基本的适配目的。
html标签的fontSize的值可在页面加载的时候通过js进行计算,动态设置设置,计算方式为:
fontSize = 当前屏幕宽度/基础设备宽度*基础设备上约定的html标签fontSize
iphone6 plus
screenWidth: 414px
html: font-size = 414/375*100px = 110.4px
div: width = 1rem = 110.4px
height = 1rem = 110.4px
以上根据屏幕尺寸计算html的fontSize值的方式能够实现的是页面在iphone6(开发基础稿)的基础上根据屏幕宽度进行简单的等比例缩放,但是却无法解决retina屏幕的问题,如果开发中以iphone6为基础,那么考虑到其dpr=2的问题,应该用宽度为750px的设计稿作为开发稿。
根据dpr做页面缩放
上面主要解释rem的适配原理,现在来解释dpr的问题。
如果忽略了dpr的不同,会有什么样的问题呢?
最主要的问题就是展示同样的大小的内容,在dpr越大的屏幕上页面元素内容会越虚,尤其是图片和1px border的问题。
原因是上面提到的dpr越大,一个逻辑像素对应的物理像素就越多,图片也会越清除细腻,反之越粗糙模糊。之所以会虚是因为展示100px*100px的图片,dpr=1的屏幕一个物理像素展示一个逻辑像素;dpr=2的屏幕四个物理像素展示1个逻辑像素,四个物理像素如何展示一个逻辑像素呢,系统在计算的时候,会把一个逻辑像素补充为4个色值,透明度等近似的逻辑像素点,这样图片展示就会显得虚。
移动开发retian屏幕常见问题
疑问思考
1.antd iphone6 1rem = 100px 6p 1rem=150rem 设置width:7.5rem,6上宽度100%,6p上无法显示宽度100%,