之前没做过移动端web开发,最近接手的一个小项目是做微信公众号,需要考虑手机适配。UI给出的设计稿是一般是基于iphone手机做的(15年之后是基于ipone6),我们的UI是基于iphone6来做的(iphone6手机的分辨率是1334 * 750)。开发过程中主要有几个疑问:
1、如何将设计稿的尺寸转换到开发尺寸?
2、如何实现自适应?
3、如何调试在手机中的展示效果
下面会针对这几个问题展开讲解。讲解之前,要先讲下移动端开发的基础知识。
一、基础知识
1、屏幕尺寸
即我们通常说的尺寸是多少多少英寸啦,指的是屏幕对角线的长度。比如iphone6的屏幕尺寸是4.7英寸
px,pixel,像素,电子屏幕上组成一幅图画或image的基本单元。
pt, point,点,印刷行业常用单位,等于1/72英寸。
ppi,pixel per inch,每英寸像素数,值越高,屏幕越细腻。
dpi, dot per inch,每英寸多少点,该值越高,则图片越细腻。
dp,dip, Density-independent pixel,安卓开发用的长度单位。以160ppi为标准,和iPhone的scale差不多的意思。安卓用dp适配,系统会自动将dp转换为px。当屏幕像素点密度为160ppi时,1dp=1px。
2、屏幕分辨率
屏幕上的像素总数。常用的表现形式如:1280x720, 1920x1080等。这个像素指的是物理像素
3、pt和px
pt:pixel,像素,电子屏幕上组成一幅图画或image的基本单元。
pt:point,点,印刷行业常用单位,等于1/72英寸。
但是你想知道的pt可能并不是指这里的pt,而是IOS系统的pt,请往下看
4、 ios pt 和 px
1)px:像素,电子屏幕上组成一幅图画或image的基本单元。
2)pt
iOS 开发中用到的单位 pt 是独立像素的意思。和安卓中的单位dp本质上是一个概念。
它是绝对长度,不随屏幕像素密度变化而变化(和我们日常用到的毫米、厘米是一个意思,只是它要小得多)。
在非视网膜的 iPhone 上(iPhone 3G),苹果规定 1px=1pt,也就是说 pt 和像素点是一一对应的。但随着 iPhone 4 的到来,高分屏出现了(视网膜屏),这个时候 1pt 对应 2px。
出现了所以用固定长度 pt 作为开发单位的好处是:这样可以统一图形在同一种类不同型号设备上图形的大小。而如果用像素作为单位的话,就乱了套了,因为在不同像素密度的屏幕里面,像素本身大小是不一样的。
5、PPI和DPI
1)PPI
设备像素密度(pixel per inch),一般手机参数中会给出ppi的值。表示每英寸像素数,值越高,屏幕越细腻。在购买手机时,ppi的值是用户关注的数据。
比如iphone 6的分辨率是1334 * 750,那PPI = (1334 ^ 2 + 750 ^ 2) ^ 1/2
通常把超过300ppi的显示屏成为retina屏。当一个显示屏像素密度超过300ppi时,人眼就无法区分出单独的像素。这也是讲:显示设备清晰度已达到人视网膜可分辨像素的极限。因此手持平板类电器显示器的像素密度达到或高于300ppi就不会再出现颗粒感。
另外观视距离及显示器尺寸的大小或许可改变上述像素密度超过300ppi的定义,因为人的观视距离在2米开外显示器像素密度只要超过200ppi也无法区分出单独的像素。
2)DPI
DPI(Dots Per Inch)最初用于衡量打印无上每英寸的点数密度,就是说你的打印机可以在一英寸内打多少个点。DPI值越小,图片越不惊喜。
当DPI的概念用在计算机屏幕上时候,和PPI是一样的。一般在IOS和Android中提到的DPI和PPI指的是同一个值
2、物理像素、设备独立像素、设备像素比
1)物理像素(physical pixel)
一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。屏幕分辨率指的就是物理像素。
2)设备独立像素(density-independent pixel)
设备独立像素(也叫密度无关像素、逻辑分辨率),可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),然后由相关系统转换为物理像素。
设备独立像素就是对应前端在开发时使用的像素单位。
所以说,物理像素和设备独立像素之间存在着一定的对应关系,这就是接下来要说的设备像素比。
3)设备像素比(device pixel ratio )
设备像素比(简称dpr,也叫倍率)定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式的得到:
设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向
在javascript中,可以通过window.devicePixelRatio获取到当前设备的dpr。
在css中,可以通过-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio和 -webkit-max-device-pixel-ratio进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。
综合上面几个概念,一起举例说明下,以iphone6为例:
设备宽高为375×667,可以理解为设备独立像素(或css像素)。
dpr为2,根据上面的计算公式,其物理像素就应该×2,为750×1334。
用一张图来表现,就是这样(图为盗图):
上图中可以看出,对于这样的css样式:
width: 2px;
height: 2px;
在不同的屏幕上(普通屏幕 vs retina屏幕),css像素所呈现的大小(物理尺寸)是一致的,不同的是1个css像素所对应的物理像素个数是不一致的。
在普通屏幕下,1个css像素 对应 1个物理像素(1:1)。 在retina 屏幕下,1个css像素对应 4个物理像素(1:4)。
如果对于设备相似比有疑问,可以参考谢泽的网络日志viewport手机逻辑像素与物理像素原理(附完整手机各版本尺寸)
二、常见手机尺寸和分辨率
iphoone手机部分是学UI网萌小秘整理的。
设备名称 | 屏幕尺寸 | PPI | Asset | 竖屏点(point) | 物理分辨率(px) | 设计分辨率(px) |
---|---|---|---|---|---|---|
iPhone X | 5.8 in | 458 | @3x | 375 x 812 | 1125 x 2436 | 1125 x 2436 |
iPhone 8+, 7+, 6s+, 6+ | 5.5 in | 401 | @3x | 414 x 736 | 1080x1920 | 1242 x 2208 |
iPhone 8, 7, 6s, 6 | 4.7 in | 326 | @2x | 375 x 667 | 750 x 1334 | 750 x 1334 |
iPhone SE, 5, 5S, 5C | 4.0 in | 326 | @2x | 320 x 568 | 640 x 1136 | 640 x 1136 |
iPhone 4, 4S | 3.5 in | 326 | @2x | 320 x 480 | 640 x 960 | 640 x 960 |
iPhone 1, 3G, 3GS | 3.5 in | 163 | @1x | 320 x 480 | 320 x 480 | 320 x 480 |
iPad Pro 12.9 | 12.9 in | 264 | @2x | 1024 x 1366 | 2048 x 2732 | 2048 x 2732 |
iPad Pro 10.5 | 10.5 in | 264 | @2x | 834 x 1112 | 1668 x 2224 | 1668 x 2224 |
iPad Pro, iPad Air 2, Retina iPad | 9.7 in | 264 | @2x | 768 x 1024 | 1536 x 2048 | 1536 x 2048 |
iPad Mini 4, iPad Mini 2 | 7.9 in | 326 | @2x | 768 x 1024 | 1536 x 2048 | 1536 x 2048 |
iPad 1, 2 | 9.7 in | 132 | @1x | 768 x 1024 | 768 x 1024 | 768 x 1024 |
andriod | 120 | @0.75x | 240*320 | 120 | ||
andriod | 160 | @1x | 320*480 | 160 | ||
andriod | 240 | @1.5x | 480*800 | 240 | ||
andriod | 320 | @2x | 640*960 | 320 | ||
andriod | 360 | @1.5x | 540*960 | 360 | ||
andriod | 360 | @2x | 720*1280 | 360 | ||
andriod | 360 | @3x | 1080*1920 | 360 |
屏幕尺寸、PPI(像素密度)、分辨率这些是可以在网上商店查询,直接查询手机的参数,比如通过中关村在线查询到的iphone6参数:
另外screen sizes网站收集了常见手机的尺寸、屏幕分辨率、PPI等数据。
但是Asset(倍率)、竖屏点(逻辑分辨率)这些通过中关村在线是查询不到的。可以通过苹果官网查看倍率、逻辑分辨率。
倍率也可以根据PPI来推算
三、根据拿到设计稿如何实现自适应
上面说过我们拿到的UI稿是基于iphone 6 (1334 * 750),设备独立像素比是2。其实对应CSS像素就是把UI稿给的宽度除2。比如设计稿的宽度是200 * 100,那么CSS像素就是 100 * 50。
但是不同型号手机的CSS像素数也不同,如何做到兼容?
在介绍方案之前,要先引入rem的概念。
1、px、em、rem
2、rem如何实现自适应
2.1 设置字题大小
首先先给页面根元素设置字体大小,可以通过js动态设置的:
var designWidth = 750, //设计稿基准手机的物理像素
clientWidth = document.documentElement.clientWidth,
designFontSize = 100, //规定设计稿基准手机下1rem等于多少物理像素,这个值最好设置整十被倍数,更加便于计算的值。如100、1000等
ratio = clientWidth / designWidth, //逻辑像素宽度和设计稿物理像素宽度比值,不同手机值不一样
fontSize = (ratio * designFontSize); //计算1rem等于多少逻辑像素
document.documentElement.style.fontSize = fontSize; //在iphone6下,计算出来是50px。即1rem = 50px。
通过上面设置后,在iphone6下,1rem = 50px逻辑像素 = 100px物理像素。
1) 为什么是clientWidth / 750?为什么要乘以100?
A.是因为这里是作为一个基础数值,换个方向去想,这里先不乘以100以免产生误解。
例如:设计稿宽度是640px,有一个元素设计稿上的宽度是50px,设备物理宽度是320px,那么我们在页面上应该设置宽度为 width:50rem,相当于宽度是:50*(320/640)=25px;这里能正确算出在320px的设备上刚好占一半,其实可以想象为 rem=(320/640)。
B.一般浏览器的最小字体是12px,如果html的font-size=(320/640)px,相当于font-size=0.5px,那么这个数值就小于12px,会造成一些计算的错误,和一些奇怪的问题,*100后,font-size是50px,就可以解决这种字体小于12px的问题。
C. 为了计算方便 我们后面把比率乘以了100,(320/640)*100,那么相对应这个元素在设置数值的时候就需要除以100了(50/100),这样可以保证最后出来的数值不变.
2)其他设置字体大小方案:
当然字体大小也可以通过媒体查询来设置。比如有些地方把html的font-size设置为625%,这样1rem = 100px。然后通过媒体查询设置不同屏幕下的的字号大小。但是这种方案的缺陷就是比较麻烦,要挨个设置。
(为什么是625%?因为大多数浏览器的默认字号是16px,因此1rem=16px,这样不方便我们px和rem转换。要让1rem = 10px,font-size应该等于10px = 16px * 62.5%。但是10px在chrome浏览器无法显示,为了兼容将font-size调整为625%。这样1rem = 100px。)。
@media screen and (min-width:360px) and (max-width:374px) and (orientation:portrait) {
html { font-size: 703%; }
}
@media screen and (min-width:375px) and (max-width:383px) and (orientation:portrait) {
html { font-size: 732.4%; }
}
@media screen and (min-width:384px) and (max-width:399px) and (orientation:portrait) {
html { font-size: 750%; }
}
@media screen and (min-width:400px) and (max-width:413px) and (orientation:portrait) {
html { font-size: 781.25%; }
}
@media screen and (min-width:414px) and (max-width:431px) and (orientation:portrait){
html { font-size: 808.6%; }
}
@media screen and (min-width:432px) and (max-width:479px) and (orientation:portrait){
html { font-size: 843.75%; }
}
2.2 将设计稿尺寸全部转换为rem
通过上面设置后,在iphone6下,1rem = 50px逻辑像素 = 100px物理像素。
因此1px物理像素 = 1 / 100 rem = 0.01rem。也就意味着设计稿给出的1px等于0.01rem。
如何将设计稿的尺寸都转换为rem呢?
交代下我们项目的背景,项目是vue框架,基于vue-cli2搭建起来。
1)方案一、scss添加转换函数px2rem
可以通过scss 的混合函数来实现。
@baseFontSize: 100;//1rem相当于多少视觉稿的物理像素
@function px2rem(@px){
@return @px / @baseFontSize * 1rem;
}
button {
width: px2rem(100px);
}
但是这种方案是不是觉得麻烦极了,每个用到的地方都要手动调用函数。别急,还有更简单的方法。
2)方案二、通过px2rem-loader实现
首先,将px2rem-loader安装到项目中。通过npm install px2rem-loader --save-dev。
px2rem-loader配置教程请参考github px2rem-loader,css样式如何控制转换规则请参考github px2rem。
找到build/utils.js,在postcss-loader前面加上px2rem-loader的设置。注意px2rem-loader是放置在sass-loader前面的,即比sass-loader后执行。
const px2remLoader = {
loader: 'px2rem-loader',
options: {
remUnit: 100
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader, px2remLoader] : [cssLoader, px2remLoader]
...
}
px2rem-loader已经可以实现自动转换了。如果不需要转换,在样式后面加上/*no*/即可。如果需要根据不同的设备像素比,添加不同的px,则在样式后面加上/*px*/。比如:
.selector {
width: 150px;
height: 64px; /*px*/
font-size: 28px; /*px*/
border: 1px solid #ddd; /*no*/
}
处理后的结果如下:
.selector {
width: 2rem;
border: 1px solid #ddd;
}
[data-dpr="1"] .selector {
height: 32px;
font-size: 14px;
}
[data-dpr="2"] .selector {
height: 64px;
font-size: 28px;
}
[data-dpr="3"] .selector {
height: 96px;
font-size: 42px;
}
当然,根据不同设备像素比设置不同的值,这个需要配合js使用。要先给元素设置不同的dpr值。这个可以利用淘宝的lib-flexible来实现,有兴趣的可以看下大漠的使用Flexible实现手淘H5页面的终端适配。大漠的文章需要付费观看,也可以看下csdn上转载的大漠文章。
px2rem-loader缺陷:
px2rem-loader有一个问题,它没有办法排除某个目录。我们项目中引入了三方模块weui,用了px2rem-loader页面就全乱了。我们想把三方模块排除在外,但是px2rem-loader并不支持。
本来想要loader自带的exclude功能,但是把px2-rem放到所有css loader前面或者后面,都会提示missing property,缺失 : 或者 }
3)postcss-px2rem-exclude
postcss-px2rem-exclude可以解决上面说的问题。
首先安装postcss-px2rem-exclude
npm install postcss-px2rem-exclude --save
然后在项目根目录下的postcss.config.js文件或.postcssrc.js配置postcss-px2rem-exclude,如果你的项目没有生成这个独立文件,就需要在你的package.json里设置。
.postcssrc.js:
module.exports = {
plugins: {
autoprefixer: {},
"postcss-px2rem-exclude": {
remUnit: 75,
exclude: /node_modules|folder_name/i
}
}
};
package.json:
"postcss": {
"plugins": {
"autoprefixer": {},
"postcss-px2rem-exclude":{
"remUnit": 75,
"exclude":"/node_modules|floder_name/i"
}
}
}
postcss-px2rem-exclude在webpack配置文件中设置无法起作用(我自己没有实践过在配置文件中添加)。具体说明请查看博客园 石耳的《vue-cli3.0结合lib-flexible、px2rem实现移动端适配,完美解决第三方ui库样式变小问题》,主要看postcss-px2rem-exclude使用部分的说明。
自适应方案也可以参考淘宝flex.js方案,引入flex.js,会根据设备像素分辨率设置根元素的字体,即1rem的大小。可以参考大漠的使用Flexible实现手淘H5页面的终端适配 。
四、手机网站
大家可以看下互联网大佬们如何实现移动端自适应:
手机网易网:http://3g.163.com/touch/#/
手机淘宝网:https://h5.m.taobao.com/
手机京东:https://m.jd.com/
五、模拟、查看手机端效果
1、浏览器调试
我们打开手淘网,登陆自己的淘宝账号。然后按F12 打开chrome开发者工具,按照途中的标准顺序号进行设置后,就可以开始调试了。
即:先选择手机调试模式--->选择要调试的设备-->选择显示百分比(显示宽度为实际手机宽度的百分多少)
2、浏览器新增调试设备
如果调试设备不满足自己要求,可以自行添加需要调试的设备。点击开发者工具右上角的更多-->设置,或按F1打开设置界面
要注意分辨率填写的是逻辑像素数。比如iphone6填写的应是375、667,而不是750、1334。
chrome设备手机模式调试能够看出大致的效果,但是有一些是模拟不出来的。比如逻辑像素1px在设备像素比高的设备看起来比较粗,字体展示效果也可能不一样。
在正式发布前,还是需要在手机端进行调试。我们做的是微信公众号,直接在手机微信中输入链接地址,就能开始调试了。
参考文章:
1、【原创】移动端高清、多屏适配方案:http://www.html-js.com/article/Mobile-terminal-H5-mobile-terminal-HD-multi-screen-adaptation-scheme%203041
2、移动端界面设计之尺寸篇(更新):http://www.xueui.cn/tutorials/app-tutorials/mobile-ui-design-size.html (含各种iphone手机尺寸)
3、前端页面适配的rem换算:https://www.cnblogs.com/liangxuru/p/6970629.html
4、一步步教你使用rem适配不同屏幕的移动设备:https://www.cnblogs.com/dannyxie/p/6640903.html (解释如何设置基准fontSize)
5、移动前端自适应适配布局解决方案和比较:http://caibaojian.com/mobile-responsive-example.html