Home  >  Article  >  Web Front-end  >  基于淘宝弹性布局方案lib-flexible的问题研究_html/css_WEB-ITnose

基于淘宝弹性布局方案lib-flexible的问题研究_html/css_WEB-ITnose

WBOY
WBOYOriginal
2016-06-21 09:00:191149browse

上篇文章《 淘宝弹性布局方案lib-flexible实践 》结合一个简单的实例,说明了lib-flexible的基本用法,但是lib-flexible的这种适配方式在适配的时候会修改viewport的initial-scale,导致viewport的width不等于device width,使得那些根据width编写的media query不一定与预期的一致,还有移动web经典的retina屏图片问题和1px边框问题在lib-flexible下也会与通常的做法有所差异,本文的内容就是研究这些东西,并提出一些自己思考和解决方法。

1. lib-flexible不能与响应式布局兼容

先说说响应式布局的一些基本认识:

响应式布局的表现是:网页通过css媒介查询判断可视区域的宽度,在不同的范围应用不同的样式,以便在不同尺寸的设备上呈现最佳的界面效果。典型的例子是,有一个商品列表页,应用响应式布局后,可能在pc上是用4列展示,在平板上用3列展示,在手机上只用1列展示。这种布局的最大好处就是节省人力、资源和时间,所以很多公司都喜欢用。而响应式布局有两个必须的要求:

1)是viewport的设置,width跟initial-scale要采用如下配置,保证viewport的宽度与device width相同:

<meta name="viewport" content="width=device-width, initial-scale=1">

2)是要利用media query,针对不同的width范围,编写不同的css,比如bootstrap:

@media (max-width: @screen-xs-max) { ... }@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { ... }@media (min-width: @screen-md-min) and (max-width: @screen-md-max) { ... }@media (min-width: @screen-lg-min) { ... }

需要注意的是:第(1)个要求提到的device width与media query里面的device-width属性表达的意思有些区别:第(1)个要求提到的device width在移动设备下指的是设备的宽度,但是在pc下指的是浏览器可视区域的宽度,比如下面这个网页,我把浏览器窗口缩小,然后你看它viewport里尽管已经把width设置成了device-width,但是网页大小却不是我的桌面的分辨率宽度(设备宽度):

media query里的device-width属性,始终指的是设备的宽度。所以响应式布局的媒介查询要用width属性,不用device-width属性,因为在桌面设备下,把浏览器窗口缩小的时候,device-width并不会发生改变,当调整浏览器窗口大小就看不到响应式的效果。

再来看lib-flexible的特点:

lib-flexible在适配的时候会修改viewport的initial-scale,导致viewport的width不等于device width。这是采用lib-flexible,在iphone6 plus下适配后,自动添加的viewport设置代码:

<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">

在这个viewport的作用下,网页的缩放系数为0.3333333333333333,iphone6 plus的device width为414个不缩放的css像素,经过缩放之后,viewport的width等于device width / 0.3333333333333333,为1242个缩放后的css像素,远远大于device width:

假如你的网页想同时使用响应式布局和lib-flexible,然后你写了一个媒介查询,需要在1024px以上的分辨率(桌面设备)呈现某个特殊样式(代码仅仅是为了举例):

@media only screen and (min-width: 1024px) {    body {        border: 10px solid #ccc;    }}

会发现这个页面在iphone6 plus下也会应用到该媒介查询的样式:

究其原因是:iphone6 plus下的网页由于lib-flexible的作用,导致页面的width与实际物理分辨率的宽相等,也就是1242个像素,完全达到了该媒介查询的范围。

所以,在使用lib-flexible的项目里很难再实现响应式布局,要是有人有这种综合两者一起使用的想法,可得注意了。实际上,这两个方案本质性的东西就不相同,适用的场景也不相同。响应式布局的目的是一套代码,能够在手机平板和pc上都能良好展现,适用于网站类的项目,而lib-flexible解决的是手机端网页的适配问题,压根不管平板和pc的情况,适用于web app类的项目。

2. 1px边框在lib-flexible下如何处理

web app有时候会设计出一些特别细的线条或者边框,如果我们直接通过css设置边框为1px:

border: 1px solid #ccc;

结果会发现这种边框在手机里看起来的效果,显得特别地粗,之所以会有这个效果,原因很简单,因为现在大部分手机的分辨率很高,一个css像素,比如上面代码中的1px,可能相当于2个甚至3个物理分辨率像素,而不像pc,一个css像素始终等于1个物理分辨率像素,所以手机里看到的1px会比实际的粗。

为了解决这个问题,你可能会想到用0.5px来代替1px,不过这个是解决不了问题的,而且带小数的像素在不同的浏览器下绝对是一个坑,千万要尽量避免。

那么通常在web app下显示1px的做法是怎样的呢,前阵子在weui的源码中看到了一个很好的办法,值得分享:

//这是一个mixin,用来设置顶部的边框,其它方向的代码没有贴出.setTopLine(@c: #C7C7C7) {    content: " ";    position: absolute;    left: 0;    top: 0;    width: 100%;    height: 1px;    border-top: 1px solid @c;    transform-origin: 0 0;    transform: scaleY(0.5);}//应用举例.weui_cell {    // onepx    position: relative;    &:before {        .setTopLine(@weuiCellBorderColor);    }}

这个办法并不是利用border属性来显示边框,而是利用了伪类和transform,最妙的是这个transform,0.5px办不到的事情,它却办得到。

由于lib-flexible在适配的时候,会缩放网页,导致css代码中的1px等于物理分辨率的1px,这样子这个1px边框的问题在经过lib-flexible适配的设备下就很好解决了,直接应用border: 1px solid;即可。但是 lib-flexible现在只适配了iphone设备,安卓设备压根没适配 :

导致在安卓设备下,1px的边框问题依然存在。所以为了在lib-flexible的项目里解决1px的问题,就得综合两种做法了:

.setTopLine(@c: #C7C7C7) {  & {    position: relative;  }  &:before {    content: " ";    position: absolute;    left: 0;    top: 0;    width: 100%;    height: 1px;    border-top: 1px solid @c;  }  [data-dpr="1"] &:before {    transform-origin: 0 0;    transform: scaleY(0.5);  }}/*照着这个可以再写setBottomLine等相关mixin*/

这个做法,我做过测试,在我devicePixelRatio为3的meilan note上,显示出的线条非常细腻,看起来很舒服,iphone的效果也不错,借别人的机子测试过,所以有兴趣的人可以借鉴使用,有问题尽管提出,看看能不能有更好的办法解决。这是我用魅蓝note做测试的截图:

第1根线是上面的mixin效果;第二根线是直接使用border: 1px solid的效果。

注:以上提到的这个1px边框的做法有三个缺点,在使用的时候需要注意:

1)它会占用掉before伪类

2)没法做圆角

3)很难实现多条边框,除非嵌套,或者再利用上after伪类。

尽管如此,以上这个做法还是非常有用的做法,因为这种细线边框属于比较特殊的设计要求,并不是每处边框都得做成这样,在开发webapp的时候用这个方法保证特殊线条的设计要求,其它的边线,我觉得直接用border并没有关系,你可以直接用你的手机打开bootstrap的官方页面,看它里面的按钮边框,效果都还不错。

3. lib-flexible如何处理retina屏下的background-image

几年之前,retina屏,也就是所谓的高清屏,还不像现在这么普遍,12年我买的第一个安卓机是华为u8825d,分辨率只有480*800,而且当时市面上大部分安卓机基本上都是这个分辨率,像iphone4 那种640*960级别的机子很少,为了解决普通背景图片在iphone4下显示模糊的问题,基本都是采用这种做法:

.css{/* 普通显示屏(设备像素比例小于等于1.3)使用1倍的图 */     background-image: url(img_1x.png);}@media only screen and (-webkit-min-device-pixel-ratio:1.5){.css{/* 高清显示屏(设备像素比例大于等于1.5)使用2倍图  */    background-image: url(img_2x.png);  }}

估计当时应该是320的设计稿,img_1x.png是在320设计稿下切出的图,然后img_2x.png是在320设计稿矢量放大2倍后切出的图,高清屏显示img_2x.png,这样就能解决当时iphone4背景图片模糊的问题。

时至今日,手机都是走高配置,低价格的发展路线,480*800这种级别的机子,市面上越来越少,大部分手机的分辨率级别都达到了iphone4的标准,比iphone4的清晰级别更高的手机也越来越多,一个800块的魅蓝note,它的devicePixelRatio都已经达到了3,原先解决background-image问题的方法,需要调整一下才能适用于现在:

.css{/* 普通显示屏(设备像素比例小于等于2)使用2倍的图 */     background-image: url(img_2x.png);}@media only screen and (-webkit-min-device-pixel-ratio:2.1){.css{/* 高清显示屏(设备像素比例大于等于2.1)使用3倍图  */    background-image: url(img_3x.png);  }}

代码中的2x和3x是相当于320的设计稿而言的,2x代替了原先的1x,3x代替了原先的2x。现在的设计稿也不再是320,而是640,2x就是在640下切出的图,3x是在640基础上矢量放大1.5切出的图。在这个代码的作用下,分辨率在640以下的设备都会显示2x的图,由于2x的图本身是在640的设计稿切出的,所以这些设备下不会有模糊的现象,在640以上的分辨率,会显示3x的图,由于3x的图是在960的分辨率下切出的,所以这种图在分辨率小于960的设备下都不会模糊。以前1x的情况根本不用再考虑了,以后不会再有需要1x图的设备,说不定过几年,市面上的手机全是devicePixelRatio在2.5以上的标准时,连2x的情况也不用考虑了。

lib-flexible在iphone6推出之后,把设计稿的尺寸提高到了750,切图时还是按2x和3x的方法来切,这样的话,经过lib-flexible适配的设备,分辨率在750以下都会显示2x的图,肯定不会模糊;分辨率在750以上的设备,会显示3x的图,也不会出现模糊。不过由于lib-flexible只适配了iphone的问题,所以我上篇博客中提到的用data-dpr来显示不同的图片的做法是错误的,因为有些安卓机,比如我的魅族node,devicePixelRatio是3,打开app页面,看到的图片却仍然还是2x的,显然达不到适配的要求,所以不能用data-dpr去适配,而应该采用下面这个做法:

.retina-image(@background-image) {  background-image: url("../img/@2x/@{background-image}?v=@@version");  background-size: 100% 100%;  background-position:left top;  @media only screen and (-webkit-min-device-pixel-ratio: 2.5),  only screen and (min--moz-device-pixel-ratio: 2.5), /* 注意这里的写法比较特殊 */  only screen and (-o-min-device-pixel-ratio: 5/2),  only screen and (min-device-pixel-ratio: 2.5),  only screen and (min-resolution: 240dpi),  only screen and (min-resolution: 2.5dppx) {    & {      background-image: url("../img/@3x/@{background-image}?v=@@version") !important;    }  }  //1dppx = 1devicePixelRatio, 1dppx = 96dpi.}

这个代码的最终效果是:

1)devicePixelRatio大于等于2.5的设备都会应用到3x图

2)其它设备都会应用到2x的图。

这个方法,在chrome的模拟器里测试过很多机型,效果不错, 不过它只适用于不使用雪碧图的背景图片 ,如果要在lib-flexible的项目里使用雪碧图作背景图片,同时又要考虑retina屏的话,需要将上面这个方法稍微改动一下。

首先看下不使用lib-flexible时,雪碧图背景在retina下是怎么做的,以腾讯的一个活动页面来说明 http://qzs.qq.com/qzone/qzact/act/qzapp/qzone5.0/mobile/index.html ,这是它在使用1x的雪碧图时某个元素的background的样式:

这是它在使用2x的雪碧图时某个元素的background的样式:

总结下它这个做法:

1)先把设计稿切出的图,合并成一张雪碧图,腾讯这个例子的设计稿是320的,所以它的切图都是1x的,这张雪碧图也就是1x的,大小为643 * 152

2)设计稿放大2倍,切图合并成一张2x的雪碧图,大小为1286 * 304

3)普清屏下只应用background-image和background-position属性,设置1x雪碧图作为背景,代码参考截图

4)高清屏下除了应用background-image和background-position属性,还要应用background-size属性,并且这个background-size的大小要设置为1x雪碧图的大小,background-position的值要与(3)里配置的值相同,代码参考截图。

如果把它做成一个mixin的话应该是类似这样的:

.retina-image(@background-image,@background-pos-x,@background-pos-y,@background-size-x,@background-size-y) {  background-image: url("../img/@1x/@{background-image}?v=@@version");  background-position:@background-pos-x @background-pos-y;  @media only screen and (-webkit-min-device-pixel-ratio: 1.25),  only screen and (min-resolution: 120dpi),  only screen and (min-resolution: 1.25dppx) {    & {      background-size: @background-size-x @background-size-y;      background-image: url("../img/@2x/@{background-image}?v=@@version") !important;    }  }  //1dppx = 1devicePixelRatio, 1dppx = 96dpi.}

考虑到1x不会再有的情况,上面这个mixin可以再调整一下:

.retina-image(@background-image,@background-size-x,@background-size-y,@background-pos-x,@background-pos-y) {  background-image: url("../img/@2x/@{background-image}?v=@@version");  background-size: @background-size-x @background-size-y;  background-position:@background-pos-x @background-pos-y;  @media only screen and (-webkit-min-device-pixel-ratio: 2.5),  only screen and (min--moz-device-pixel-ratio: 2.5), /* 注意这里的写法比较特殊 */  only screen and (-o-min-device-pixel-ratio: 5/2),  only screen and (min-device-pixel-ratio: 2.5),  only screen and (min-resolution: 240dpi),  only screen and (min-resolution: 2.5dppx) {    & {      background-image: url("../img/@3x/@{background-image}?v=@@version") !important;    }  }  //1dppx = 1devicePixelRatio, 1dppx = 96dpi.}

默认用2x的图,devicePixelRatio大于等于2.5的设备用3x的图。这个调整后的mixin就是lib-flexible下,使用雪碧图背景的方法,应用举例:

@font-size-base: 75;.btn-android {  .retina-image("sprite.png", 414rem/@font-size-base, 232rem/@font-size-base, 0, -64rem/@font-size-base);}

sprite.png用750设计稿的切图合并后的大小是414 * 232,.btn-android这个按钮的position为0 –64px。

尽管这个方法看起来完美,但是不建议使用,因为它的适配效果不好,这是iphone6下的效果:

看起来不错,那是当然的,因为这就是它默认没有任何缩放的效果。然后看iphone6 plus的效果:

有点差异,但好像还能接受。再看看nexus6的效果:

这就不能忍了,样式差的离谱。造成这个差异的原因也很简单,就是rem的副作用,腾讯的页面里所有position,size都是不带小数的数值,而且2x跟1x之间是整数的翻倍,而不是3x跟2x之间的1.5倍,lib-flexible会导致大部分的设备下position和size都是小数数值,所以很难保证背景图片缩放后还能通过position显示到正确的位置:

从网页优化的角度来说,减少请求数,减少请求数据大小是两个基本的思路,雪碧图就是一个减少请求数但是不能减少请求数据量的方法。lib-flexible不能使用兼容3x屏的雪碧图的情况看起来是它一个大的缺陷,但实际上也并非如此:雪碧图如果用不了,就采用别的思路来优化,我能想到的更好的就是图片的延迟加载和懒加载,在app页面里控制好默认只加载首屏的图片,并且采用延迟和懒加载的方式,避免阻塞页面的加载,也能有极好的用户体验,打开手机淘宝的页面给人的感觉就是如此,而且你去看看手机淘宝的应用会发现它根本就没有用雪碧图,但是速度还是很快。

4. 结束语

本文主要研究了lib-flexible与响应式布局,1px边框和retina屏里背景图片适配的问题,希望能对你使用lib-flexible有所帮助,文中提出的方法不一定是最好的办法,而且可能还有遗漏的情况未考虑到,尤其是最后的背景图片问题,仅仅是webapp里图片处理的一个细小分支,有不对的地方,请您多多指正!

谢谢阅读:)

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn