后台开发手机H5页面面向不同分辨率的手机面向不同屏幕尺寸的手机视觉稿前端开发前,视觉MM会给我们一个psd文件,叫做mockup。对于移动端开发,为了达到高清的页面效果,视觉稿的规格往往遵循以下两点:一是选择手机屏幕的宽高作为基准(之前是320×480iPhone4,现在更多的是iphone6的375×667)。对于retina屏幕(如:dpr=2),为了达到高清效果,mockup的canvassize会是reference的两倍,也就是说像素数会是原来的四倍(iphone6:原来的375×667变成750×1334)。问题:对于dpr=2的手机,canvassize×2为什么可以解决高清问题?对于2倍尺寸的视觉稿,在具体的css编码(即布局问题)中如何还原每块的真实宽高?带着问题往下看……在具体分析一些概念之前,我们首先要知道以下几个关键的基本概念(术语)。物理像素(physicalpixel)物理像素是显示器(手机屏幕)上最小的物理显示单位。在操作系统的调度下,每个设备像素都有自己的颜色值和亮度值。Device-independentpixel(密度无关像素)device-independentpixel(也叫密度无关像素),可以认为是计算机坐标系中的一个点,这个点代表一个虚拟像素(如:css像素),它可以由程序使用,然后由相关系统转换为物理像素。因此,物理像素和设备独立像素之间存在一定的对应关系,这就是接下来要讨论的设备像素比。设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系,其值可以根据以下公式得到:设备像素比=物理像素/设备独立像素//在某个方向上,x方向或y方向在css中,可以通过-webkit-device-pixel-ratio、-webkit-min-device-pixel-ratio和-webkit-max-device-pixel-ratio进行媒体查询,针对不同的dpr设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。在javascript中,可以通过window.devicePixelRatio获取当前设备的dpr。基于以上概念,我们来举例说明——以iphone6为例:设备的宽高为375×667,可以理解为设备的独立像素(或css像素)。dpr为2,根据上面的计算公式,它的物理像素应该是×2,也就是750×1334。用一张图来表达,就是这样(原谅我盗图):从上图可以看出,对于这样一个css样式:width:2px;height:2px;在不同的屏幕上(normalscreenvsretinascreen),csspixel呈现的大小(物理尺寸)是一致的,不同的是一个csspixel对应的物理像素个数不一致。在普通屏幕上,1个css像素对应1个物理像素(1:1)。在视网膜屏幕下,1个css像素对应4个物理像素(1:4)。位图像素位图像素是光栅图像(如:png、jpg、gif等)的最小数据单位。每个位图像素都包含一些自己的显示信息(如:显示位置、颜色值、透明度等)。说到这里,就不得不说说retina下图片的显示?从理论上讲,一个位图像素对应一个物理像素,可以完美清晰地显示画面。普通屏下没问题,但retina屏下会出现位图像素不足,导致画面模糊。用一张图来表示:如上图所示:对于dpr=2的视网膜屏幕,1个位图像素对应4个物理像素。由于单个位图像素无法进一步划分,只能就近选择颜色。导致图片模糊(注意上面说的几个色值)。所以,对于图片高清的问题,比较好的解决办法是将图片放大一倍(@2x)。例如:200×300(css像素)img标签,需要提供400×600的图片。这样,位图像素数是原来的四倍。在retina屏幕下,位图像素数可以和物理像素数形成1:1的比例,画面自然会清晰(这也解释了之前留下的一个问题,为什么mockup的canvassize×2?)。这里还有一个问题。如果在正常屏幕上使用两倍的图片会怎样?显然,在普通屏幕上,200×300(css像素)img标签对应200×300个物理像素,而2倍图片的位图像素数为200×300*4,所以1个物理像素对应4个位图像素,所以它的选色只能通过一定的算法(显示结果只有原图像素总数的四分之一,我们把这个过程叫做下采样),虽然肉眼看图片没有模糊,但是会感觉画面缺少一些清晰度,或者有一点色差(但还是可以接受的)。用一张图来表示:针对以上两个问题,我做了一个demo。demo中将100×100张图片分别放置在100×100、50×50、25×25的img容器中,以及在retina屏幕上的显示效果。在条形图中,通过放大镜其实可以看到边界像素值的差异:图1,颜色选在附近,颜色值介于红白之间,偏淡,图片看起来比较模糊(可以理解为图片拉伸)。图2中没有附近的颜色选择,颜色值不是红色就是白色,画面看起来很清晰。图3,最接近的颜色,色值介于红白之间,偏重,图片看起来色差,锐度不足(可以理解为图片挤压)。情字图,看“情”字就可以区分图片是模糊还是清晰(不明显请下载原图)。#p#几个问题下面说说大家在移动端H5开发中不同分辨率不同屏幕会遇到的一些经典问题。retina下,高清图片的问题上面已经介绍过解决方法:将图片放大一倍(@2x),然后将图片容器缩小50%。如:图像尺寸,400×600;1.imglabelwidth:200px;height:300px;2.backgroundimagewidth:200px;height:300px;background-image:url(image@2x.jpg);background-size:200px300px;//or:background-size:包含;这种缺点很明显,在正常画面下:@2x的图片也被下载了,造成资源浪费。由于下采样,图像将失去一些清晰度(或色差)。所以最好的解决办法是:在不同的dpr下,加载不同大小的图片。不管是通过css媒体查询还是javascript条件判断,都是可以的。那么问题来了,这样的话,不就是要准备两套图片吗?(@1x和@2x)我觉得好的公司都会有这样的图片服务器,可以通过url获取参数,进而控制图片的质量,还可以将图片裁剪成不同的大小。所以我们只需要上传大图(@2x),其余的小图都交给图片服务器处理。我们只需要负责拼接url即可。比如这样一张原图可以这样裁剪://200×200//100×100(ps:当然裁剪只是对原图按比例裁剪,所以图像一定要清晰~)Retina,border:1px这大概是设计师最敏感最关心的问题了。首先不得不说,为什么border:1px存在于retina下?我们正常写css,像这样border:1px;,在retina屏幕下会不会有问题?首先我们来看下图:上面两张图分别是iPhone3gs(dpr=1)和iPhone5(dpr=2)下的测试结果。相比之下,对于1px边框的显示,它们是一致的,没有区别。那么视网膜显示器有哪些优势呢?为什么设计师会觉得高清屏下的线(右图)比较粗?明明左右是一样的~我通过一张图来解释一下(原谅我又盗图了):上图中,对于一条宽度为1px的直线,它们的物理尺寸(灰色区域)屏幕上确实是一样的,不同的其实是屏幕上最小的物理显示单位,也就是物理像素,所以对于一条直线来说,iPhone5能显示的最小宽度其实就是由图中红圈,用CSS表示,理论上是0.5px。因此,边框:1px;设计师想要的视网膜下实际上是1个物理像素宽度。对于CSS来说,可以认为是border:0.5px;,这是retina下可以显示的最小单位(dpr=2)。然而无奈,并不是所有的手机浏览器都能识别border:0.5px;,在ios7、android等系统下,0.5px会被当成0px处理,那么这个0.5px如何实现呢?最简单的方法是这个(元素比例):.scale{position:relative;}.scale:after{content:"";position:absolute;bottom:0px;left:0px;right:0px;border-bottom:1pxsolid#ddd;-webkit-transform:scaleY(.5);-webkit-transform-origin:00;}我们写border-bottom:1pxsolid#ddd;和往常一样,然后通过transform缩小0.5倍:scaleY(.5)来达到0.5px的效果,但是这种hack不够通用(比如:圆角等),比较麻烦来写。当然还有很多其他的hack方法,网上可以搜到,但是各有优缺点。这里推荐的方案是页面缩放方案,比较通用,几乎可以满足所有场景。对于iphone5(dpr=2),添加如下meta标签,设置viewport(scale0.5):这样,页面中所有border:1px都会缩小0.5,从而达到border:0.5px的效果;.看一下实现后的效果图对比(右图是优化后的):但是页面缩放不可避免地会带来一些问题:字体大小会缩放,页面布局也会缩放(如:div的宽高等)这两个问题后面会提到...#p#多屏适配布局问题移动端的布局,为了适配各种大屏手机,最好的解决方案是使用相对单位rem。基于rem的原理,我们要做的是:针对不同的手机屏幕尺寸和dpr,动态改变根节点html的font-size(基线值)。这里我们提取一个公式(rem代表参考值)rem=document.documentElement.clientWidth*dpr/10乘以dpr,因为页面可能缩放(scale)1/dpr倍以达到1px的边框(如果不是,dpr=1).说明:除以10是为了四舍五入,方便计算(理论上可以取任意值),所以html的font-size可能如下:iPhone3gs:320px/10=32pxiPhone4/5:320px*2/10=64pxiPhone6:375px*2/10=75px对于动态改变根节点html的font-size,我们可以通过css或者javascript来实现。css模式下,可以通过设备宽度的媒体查询改变html的font-size:缺点:通过设备宽度范围区间等媒体查询动态改变rem参考值不够准确,例如:width为360px和width320px的手机,因为屏幕宽度在同一范围内(<375px),所以会被同等对待(rem基准值相同),但实际上它们的屏幕宽度并不相等,它们的layouts也应该不同。最后得出的结论是:这种方法不够准确,但足够了。javascript方法,通过上面的公式,计算出参考值rem,然后写样式,大致如下(代码参考kimi的m-base模块):vardpr,rem,scale;vardocEl=document.documentElement;varfontEl=document.createElement('style');varmetaEl=document.querySelector('meta[name="viewport"]');scale=1/dpr;dpr=win.devicePixelRatio||1;rem=docEl.clientWidth*dpr/10;//设置视口和缩放达到高清效果metaEl.setAttribute('content','width='+dpr*docEl.clientWidth+',initial-scale='+scale+',maximum-scale='+scale+',minimum-scale='+scale+',user-scalable=no');//设置data-dpr属性,为csshack保留docEl.setAttribute('data-dpr',dpr);//动态writingstyledocEl.firstElementChild.appendChild(fontEl);fontEl.innerHTML='html{font-size:'+rem+'px!important;}';//调用给js,a下rem和px的转换函数某些dprwindow.rem2px=function(v){v=parseFloat(v);returnv*rem;};window.px2rem:function(v){v=parseFloat(v);returnv/rem;};window.dpr=dpr;window.rem=rem;这种方法可以准确计算出不同屏幕的rem参考值。缺点是需要加载这么一段js代码,但个人认为这是目前最好的解决方案。因为这个方案同时解决了三个问题:border:1px问题图片高清问题屏幕适配布局问题说到布局自然要回答原问题:如何还原视觉稿中的真实宽度css编码高?前提条件:得到的是一个iPhone6高清样机,750×1334,使用上述高清方案(js代码)。如果有块,在psd文件中测量:一个宽高为750×300px的div,如何转换成rem单位?公式如下:rem=px/参考值;对于一个iPhone6的视觉稿,它的参考值是75(前面提到过);所以,在确定了视觉稿(也就是确定了参考值)之后,通常我们会使用Less这样写一个mixin://例如:.px2rem(height,80);.px2rem(@name,@px){@{name}:@px/75*1rem;}所以,对于宽高为750×300px的div,我们用less这样写:.px2rem(width,750);.px2rem(height,300);转成html就是这样:width:10rem;//->750pxheight:4rem;//->300px最后因为dpr为2,页面缩放比例为0.5,所以手机屏幕显示的真实宽高应该是375×150px,刚刚好。如果页面没有0.5的比例,我们的代码必须是这样的:.px2rem(width,375);.px2rem(height,150);这样的宽高,我们经常会这样得到:750×1334的视觉稿,转换为375×667的尺寸后,测量这个块的尺寸(感觉好傻)。测量完750×1334的块的宽高是750×300px,再除以2(感觉很麻烦)。最后给出一张不进行布局适配(上)和rem布局适配(下)的对比图:(以上手机分别为:iPhone3gs、iPhone5、iPhone6)很明显rem适配后每块的宽高会随着变化手机的屏幕宽度。最明显的是看图片列表的部分。最后一张图片的视觉稿只需要一点点就可以出现。rem布局在任何屏幕上都能很好地显示。字体大小问题由于上面的方案会使页面缩放(scale),所以页面块的宽高我们可以依赖高清样机,因为样机已经是×2了,我们直接测量即可,那么字体应该怎么处理呢?对于字体缩放问题,设计者最初的要求是这样的:任何手机屏幕上的字体大小必须是统一的,所以我们会针对不同的分辨率(dpr不同)做如下处理:font-size:16px;[data-dpr="2"]input{font-size:32px;}(注意rem不能用于字体,误差太大,不能满足任何屏幕下相同的字体大小)为了方便,我们还将用less写一个mixin:.px2px(@name,@px){@{name}:round(@px/2)*1px;[data-dpr="2"]&{@{name}:@px*1px;}//formx3[data-dpr="2.5"]&{@{name}:round(@px*2.5/2)*1px;}//对于小米note[data-dpr="2.75"]&{@{name}:round(@px*2.75/2)*1px;}[data-dpr="3"]&{@{name}:round(@px/2*3)*1px}//forSamsungnote4[data-dpr="4"]&{@{name}:@px*2px;}}(注:html的data-dpr属性在之前的js方案中有提到,这里有用)根据体验和测试,还是会有这些奇怪的dprs,这里统一兼容~使用的时候是这样的:.px2px(字体大小,32);当然,对于其他的css属性,如果你也要求它们在不同的dprs下保持一致,你也可以这样做,比如:.px2px(padding,20);.px2px(right,8);最后总结了一些手机H5高清多屏适配的解决方案上面,还有知识讲解,不对的地方还请大家指出。
