移动适配页面实践与总结

布局

lib-flexible

这是手淘团队开源的一个基于viewport与rem单位的H5适配开源库,原理非常的简单,通过手机dpr动态设置viewport的缩放以及html的font-size来兼容不同尺寸屏幕下页面的效果。

在100分的项目中,我们没有采取他默认的缩放加rem的方式,强制定义缩放比例为1,只使用了rem方案来适应不同的手机。查看lib-flexible代码,发现只要手动设置了meta标签,就会按照自己的缩放比例进行计算。

// flexible源代码第11行,判断缩放的方式
var metaEl = doc.querySelector('meta[name="viewport"]');
if (metaEl) {
        console.warn('将根据已有的meta标签来设置缩放比例');
        var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (match) {
            scale = parseFloat(match[1]);
            dpr = parseInt(1 / scale);
        }
} else if (flexibleEl) {
        // .......
}

在使用lib-flexible的时候,由于水土不服等多种原因,遇到了很多坑,如果有要采用同样方案的小伙伴,可以重点注意一下。

  • 图标:如果没有自定义,android的dpr强行被设置为了1,也就是说,android和ios用了两套不同的方案,这会带来什么样的后果?刚才介绍过,该库是通过手机dpr动态设置viewport的缩放。那么,也就是说在android手机上面无法通过缩放来达到最好的效果,1倍图在dpr2,3下的显示效果视觉看到会来找你的。但是我们发现android中淘宝的图片是正常的,因为他首页的每张图片都是一个请求,并且类似于img,通过rem控制显示的大小。如图:
image

100分没有采取此方案,而是采取了简单粗暴的雪碧图方案,下文细说。

  • iframe:在100分的项目中,强行设置了他的缩放比例,如果页面中有iframe的话,iframe同样会被缩放,众所周知,URS登陆组件本质就是一个iframe,我们可以修改其中的样式来达到自己想要的效果,问题在于我们无法修改iframe上根html的font-size,因此缩放iframe时,无法自动变换iframe中各个属性的大小。如图:
image

上图中,因为父页面使用了缩放,导致iframe内容也同时做出了响应了缩放,但是因为iframe子页面无法设置font-size,因此显示非常奇怪。

  • 文字:在lib-flexible中,建议继续使用px作为文字的单位,因为缩放viewport的原因,需要对不同dpr下的手机设置不同的font-size,需要配合属性选择器使用。

  • 通常视觉GG/MM们给出的都是视觉稿都以640、750px分辨率为标准,其中都是以像素单位来标注盒子模型的各个属性,如果基准font-size100px的话,计算起来到还方便,但像lib-flexible他的根font-size设置的公式是:(手机分辨率/10),即750下font-size为75px,在这个数值下转换px和rem的话,会增加前端的计算量。因此,100分使用了scss中的宏来自动转换,如有需要的童鞋,可以直接拷贝使用:


$rem-baseline: 75px !default;
@function rem-separator($list) {
    @if function-exists("list-separator") == true {
        @return list-separator($list);
    }

    $test-list: ();
    @each $item in $list {
        $test-list: append($test-list, $item, space);
    }

    @return if($test-list == $list, space, comma);
}

@mixin rem-baseline($zoom: 100%) {
    font-size: $zoom / 16px * $rem-baseline;
}

@function rem-convert($to, $values...) {
    $result: ();
    $separator: rem-separator($values);
    @each $value in $values {
        @if type-of($value) == "number" and unit($value) == "rem" and $to == "px" {
            $result: append($result, $value / 1rem * $rem-baseline, $separator);
        } @else if type-of($value) == "number" and unit($value) == "px" and $to == "rem" {
            $result: append($result, $value / ($rem-baseline / 1rem), $separator);
        } @else if type-of($value) == "list" {
            $result: append($result, rem-convert($to, $value...), $separator);
        } @else {
            $result: append($result, $value, $separator);
        }
    }
    @return if(length($result) == 1, nth($result, 1), $result);
}

@function px2rem($values...) {
    @return rem-convert(rem, $values...);
}

@mixin px2rem($properties, $values...) {
    @if type-of($properties) == "map" {
        @each $property in map-keys($properties) {
            @include px2rem($property, map-get($properties, $property));
        }
    } @else {
        @each $property in $properties {
            #{$property}: rem-convert(rem, $values...);
        }
    }
}

// 使用方式
.login_box {
    @include px2rem(margin, 0 auto);
    @include px2rem(padding, 20px 0 15px);
}

刚才说到,100分项目采取了强制设置initial-scale=1的方式来按照视觉稿还原,这么做也有一些无法解决的问题。

  • 1px问题,先看一张图:
    image

    这是在不同dpr手机下显示1像素的实际像素图。我们知道在2倍retina屏幕上相对于普通屏幕,1个像素需要4个物理像素来组成,这样才能达到高清的作用,如果视觉GG/MM在2倍稿子之中画出了1px的线,即在视觉640分辨率下,1px实际显示显示的是红线圈出来的灰色区域,如果在initial-scale=1情况下看,对应css应该为0.5px,对部分android机器而言,会将0.5px识别为0,因此这里一般有一个hack
    transform:scaleX(0.5)或transform:scaleY(0.5)

这个hack也只是为了视觉而做出的无奈之举,解决这个问题的关键在于去交流

  • 如果你的视觉设计师要求严格,对该界面把控非常严格的话,应该动态去使用scale的hack去改变。

  • 如果部分区域实在没有必要过度调整,和视觉GG/MM交流一下更好。

  • px最好用双数。

flex + box-sizing

  • 弹性盒子作为布局神器可以完成很多富有创造力的布局,只要记住常用的属性,用起来特别的方便。

  • box-sizing,box-sizing作为css3的属性,给前端人员的尺寸测量带来了极大的便利性。没有用这个属性之前,我们设置宽度的时候,总是要去除margin,padding来计算,使用它后,不用再单独计算宽度和间距,全部根据视觉稿测量的为准。

起初采用此方案时,担心flex布局的兼容性,特意把flex的大部分属性进行了一次测试,发现一切安好。
测试系统:IOS 8IOS`` 9android 4.2android 4.4android 6.0
有其他需求的话,可以自行用手机测试(只测试了常用的属性,欢迎提交其他属性测试)
测试地址

  • 在华为的部分手机上发现flex不支持行内元素,必须改成块级元素才能被支持。
  • 预留位置(目前适配页面不多,还没遇到其他问题, 后期补充)。

图片

雪碧图

为什么在100分项目中执迷于采用雪碧图方案呢,在图片方案选择中明明还有不少其他方案,如淘宝的每个图标就是一张大图的方案,图标采取base64方案在通过background-size缩放,字体图标方案。

  • 字体图标方案应该是最好的解决方案了,能非常契合的和rem布局配合,能完成不同场景下显示效果一致的问题。难度在于,做字体工作量巨大,让视觉去做这么多矢量图,在转成svg与字体文件,视觉不愿意去做。

  • 淘宝每个图标一张大图,在任何dpr的机型之中,都对应的同一张图片,好处了通过rem的方式,在任何尺寸下都能完美的还原视觉稿,但同时给对用户流量造成了不必要的开支,即在dpr1的pad上面,无论如何都是请求最高分辨率的图片。同时,每张图片一个请求不符合减少HTTP请求这一条优化原则。因此没有采用。

  • base64图片少还好,如果图片多的话,尤其是在于retina这种需要双倍图的情况下,会大大增加css文件的大小(gzip压缩base64字符也并不明显),如果css过大,还不如增加一个请求来请求图片资源。如果用了自动化工具还好,一旦没有使用自动化工具,代码混乱且难以维护。这种方式比较适合图片小而且不经常更换的场景,因此该方案不能作为主体方案。

说了这么多其他方式,说说100分采取的传统雪碧图方案,相对于传统雪碧图的繁琐。在100分项目中,采取的通过compass的方式进行动态生成,并且为了兼容retina屏幕,使用了2套不同的图片进行生成,在通过媒体查询的方式确定当前手机需要访问什么样的图片资源。在利用scss强大的mixin,完成自己想要的功能。
相关代码和项目结构如下:

    
//生成compass雪碧图    
@function generate-sprite($path){
    @return sprite-map($path, $spacing: 10px, $layout: vertical);
}
// 获取某一张图的background-position
@mixin sprite-background($name,$normal,$retina:null) {
    background-repeat: no-repeat;
    background-image: sprite-url($normal);
    background-position: sprite-position($normal, $name);
    height: image-height(sprite-file($normal, $name));
    width: image-width(sprite-file($normal, $name));

    // retina屏幕样式
    @if ($retina != null){
        @media (min--moz-device-pixel-ratio: 1.3),
        (-o-min-device-pixel-ratio: 2.6/2),
        (-webkit-min-device-pixel-ratio: 1.3),
        (min-device-pixel-ratio: 1.3),
        (min-resolution: 1.3dppx) {
            background-image: sprite-url($retina);
            background-position: 0 round(nth(sprite-position($retina, $name), 2) / 2);
            height: round(image-height(sprite-file($retina, $name)) / 2);
            width: round(image-width(sprite-file($retina, $name)) /2);
            $double-width: ceil(image-width(sprite-path($retina)) / 2);
            $auto-height: auto;
            @include background-size($double-width $auto-height);
        }
    }
}

//调用方式, 写起来非常的简单
/* 设置不同倍数的雪碧图*/
$sprites: generate-sprite("mobile/mob/member/login/icon1x/*.png");
$sprites-retina: generate-sprite("mobile/mob/member/login/icon2x/*.png");

.icon-logo {
    @include sprite-background(logo, $sprites, $sprites-retina);
}

项目结构:

image

根据不同dpr取不同的图片

生成的图片样式如下:

图片

觉得不好看浪费空间的话可以设置排列方式。反正不用自己量,who care?

该方案灵活简单,雪碧图交给compass生成,增加修改删除测量都是全自动化的,非常的易于使用以及后期维护,同时也兼容了高清图的需求。有一个缺点就是不能100%还原视觉稿,在任何手机下看到的图标大小都是固定的。具体采取以上什么方式,还是要根据当前开发环境进行分析。

字体

安利一个简单的字体图标生成网站,可以自己上传矢量图生成图标,也可以使用网站提供的图标,贴心的是还会生成该图标使用的css。地址

总结

总的来说,移动适配还不存在万能的技术方案,无论是字体、间距、布局、图片都还需要根据自己项目的环境以及复杂度来进行选型。

参考资料

  1. 使用Flexible实现手淘H5页面的终端适配
  2. 使用Sass和Compass制作雪碧图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容