imweb / mobile

library for mobile develop
225 stars 39 forks source link

rem自适应方案 #3

Open longyiyiyu opened 9 years ago

longyiyiyu commented 9 years ago

H5自适应改造方案——rem方案

原理

之前我们H5页面使用的方案是设置viewport meta头,让浏览器帮我们把页面缩放,使得页面看起来好像是自适应。实际上这不是自适应,只是在中小屏设备上,看起来差距不大,问题不明显而已。

现在大屏高清设备流行,自适应的问题也越来越明显了。在大屏设备上,课程封面小,title被拉长,文字小,按钮扁等问题越来越突出,已经达到不能忍受的程度,因此需要一套自适应的方案。

页面缩放

在viewport meta头里,取消让浏览器自动缩放页面,页面的自适应由页面自身管理。 因为有dpr的问题,为了统一管理,让浏览器帮我们根据dpr缩小页面,比如dpr=2,就缩小0.5让页面统一处在dpr=1的环境中。

rem

rem是一个半相对单位,它相对的是html(或body)元素的font-size值,例如有html { font-size: 10px; },则1rem = 10px。

当html元素的font-size是根据设备宽度自适应时,使用rem的页面也就会有自适应的特性。

文字

根据以下两个原因,对于文字使用px:

没有浏览器帮助我们缩放页面,在高清设备里面需要我们手动对文字进行缩放,为了方便这个而不需要写media query,页面在计算html的font-size值的同时,会设置一个data-dpr的属性。这时文字的media query就可以这样写:

p { font-size: 12px; }  /* dpr = 1 */
[data-dpr="2"] p { font-size: 24px; }   /* dpr = 2 */
[data-dpr="3"] p { font-size: 36px; }   /* dpr = 3 */

规范

  1. 在head头引入初始化js脚本,可以使用两种代码:
    • 淘宝移动端自适应方案开源代码,注:这里的因子是10,即font-size = W/10
    • 腾讯课堂公众号最新代码里面(路径:src/inline/rem.html),里面的代码是淘宝现网代码copy下来的,与开源代码对比过,大致一样。注:这里的因子是16,即font-size = W/16
  2. 开发时,假如视觉稿宽度是640,则最好使用第二种方案,选择16作为因子,则比例为640/16=40。那么,页面所有的rem数值的换算公式为:在视觉稿中的px数值/40。

    以后会开发一个fis插件,在开发时只需填入视觉稿中的px数值,由fis插件帮助我们自动换算,在插件开发出来之前,先人工计算吧。

  3. 文字使用px单位,同时需要写media query,借助html元素上面的data-dpr属性,详见原理部分。
miniflycn commented 9 years ago

good job,解决了我周四的问题

longyiyiyu commented 9 years ago

实践所得——重构公众号首页

  1. 在重构前,针对设计稿评估是否真的需要用到rem方案,如果存在以下两点都需要考虑使用rem方案
    • 稀疏布局 由前面的【原理】可知,非自适应的页面通过浏览器的缩放在不同手机之间的显示差不多,关键只在于这些差异能不能容忍,如果可以容忍,那么完全可以不用自适应方案。 稀疏布局不像列表结构,页面元素不多。其中某些元素需要宽度自适应(否则太窄),这时高度也必须跟着自适应(否则太扁),那么里面的字体也必须跟着自适应(否则太空旷,除非多几行)
    • font-size < 12px font-size小于12px的字体在3倍屏下面会显得很小
  2. 一般来说,640的稿子量出来的字体是data-dpr="2"的,750(ip6)也是data-dpr="2"的,data-dpr="1"和data-dpr="3"的按倍数计算,同时可以做一下微调。 一个640px宽的屏幕,它可能是6401,也可能是3202,不管是哪一种,页面的布局(使用rem单位)长得都是一样的,那字体要怎么度量?这个问题放在以前就是一个@medie的事情,但是现在不想这样做,希望通过[data-dpr="xx"]来实现,只有2个临界点,那么问题是,你怎么知道640的页面是1dpr还是2dpr?这个只能通过普遍机型的经验来确定,一般640,750都是2dpr的,因此对于640的稿子量出来的字体大小是属于data-dpr="2"的。 如果我们的页面需要适应pad,那么有两个方案,一个是定宽;另一个就是在html元素上面再加上一个data-device了,因为640*1会在pad中出现。
  3. 图片需要3倍高清,需要视觉稿提供,多倍图(包括课程封面)注意
    • css实现时,可以和font-size的处理一样,通过[data-dpr="xx"]来设置不同的图片
    • js实现也可以通过html的data-dpr属性值来设置。
    • 首屏图片最好用css,使用background,如果需要img,则只能内嵌js代码
    • 非首屏图片的话img和background都可以用,在lazyload里面实现
  4. 小数点问题
    • 导致 |width - background-size| = 1,因此会出现截边问题,因此需要设置background-size: cover。因为只相差1px,对background-position影响不大,因此依然使用rem
    • 用css拼出来的图标也会出问题。图标问题有3个方案:
      • 图片:需要多倍图,在一些性能要求高的页面需要在不同屏使用不同清晰度的图片,多倍图维护起来是很麻烦的。
      • svg:android 2.3不支持,什么时候可以抛弃android 2.3,就可以使用svg了。
      • icofont:只支持灰度图,较简单,需要svg制作,比较麻烦。 相比较而已,还是支持icofont方案,原因如下:
      • 大小,颜色,底色可控
      • 支持内嵌与外链两种方式,而且在移动端,可以避免pc端的许多问题
  5. 使用icofont有以下建议:
    • 把字符资源当成一般的图片资源即可
    • 需要共享使用外链,不需要使用内嵌
    • 外链时,字符文件用gzip压缩
    • 只需要trueType(.ttf)格式
    • 一个字符资源不要包含太多图标,测试时,3个5k,10个7k
  6. js计算css的时候,需要完全动态计算,结果不一定需要使用rem单位,如果需要使用rem单位,则记住换算公式:1rem = W/16px
  7. 不需要使用rem的属性(为以后的自动计算工具做准备)
    • font-size
    • border系,除了border-radius
  8. 自动计算工具设计
    • 配置:
      • 设计稿宽度,默认640
      • rem换算因子,默认16
      • dpr,默认2
      • 不自动转换为rem的属性正则,默认[/^font-size$/, /^border/]
      • 自动转换为rem的属性正则,默认[/^border-radius$/]
    • 逻辑
      • background-size自动设置为background-size: cover;
      • 所有的px单位都转换为相应的rem,除了配置项
      • 所有包含font-size的规则,补齐另外2种屏的值
    • 规范
//规范在实现时可改
.test {
    font-size: 12px#r;  /* 转换为rem */
    margin: 5px#nr;     /* 不转换为rem */
}

.test-a {
    font-size: 12px#22|34;
}
//自动生成以下代码,假设配置dpr: 1
.test-a { font-size: 12px; }
[data-dpr="2"] .test-a { font-size: 22px; }
[data-dpr="3"] .test-a { font-size: 34px; }
litten commented 9 years ago

good!很好的分享实践

ousiri commented 9 years ago

为什么font-size和border不需要设置rem?

herbertliu commented 9 years ago

@ousiri 这里主要是考虑后续我们希望能够直接通过工具生成rem或者font-size。

xrds commented 9 years ago

为什么咋 安卓上 布局会变大阿 怎么解决阿

herbertliu commented 9 years ago

@xrds 具体指哪里?详细描述下场景,展示和屏幕尺寸有关。

xrds commented 9 years ago

@herbertliu dpr的问题 第一次用不太了解。。。 谢谢

herbertliu commented 9 years ago

@xrds dpr本身是表示显示像素和物理像素的比。一般我们用的是2倍像素,这里借助data-dpr设置,来手动管理手机分辨率(再去掉了meta viewport二倍像素设置后)。根据这个data-dpr的设置,可以在css中设置对应的想要设置的值,如: image

xrds commented 9 years ago

@herbertliu 谢谢,河伯详细讲解 我现在已经有了大概的了解了。。。

luckymore commented 9 years ago

好腻害,,,这能写个插件刁刁哒

qc-zhan commented 9 years ago

background-position用rem定位雪碧图的话会出现偏差,请问你们是怎么处理了,是用px加dpr?

herbertliu commented 9 years ago

@luckymore 插件我们看看尽快能否输出

luckymore commented 9 years ago

@herbertliu 划擦O(∩_∩)O,,,河伯大爱无疆,期待

luckymore commented 9 years ago

@qc-zhan 图片大小不是确定的么。。。为啥不用px定位,rem控制大小background-size。 -----我是一枚小菜鸟,萌的不要不要的

herbertliu commented 9 years ago

@qc-zhan 图片问题@longyiyiyu回头再补充一下, 图片一般不用rem,直接用backgrunt-size 来解决

herbertliu commented 9 years ago

@luckymore 因为rem是比例的,会出现小数点(0.3px之类的),浏览器不支持的话,就会默认成1px

qc-zhan commented 9 years ago

@herbertliu 目前我自己的方案是用px,然后根据dpr控制background-size 和 background-position,不知你们是否也是这样,还是有更好的解决方案

qc-zhan commented 9 years ago

@luckymore 河伯也说了rem是等比例的,雪碧图定位用rem的话,会出现错位的

luckymore commented 9 years ago

@qc-zhan 有个疑问,,,按比例的话,div之类的宽高不会错位么

qc-zhan commented 9 years ago

@luckymore 目前说的错位是我之前把background-position用rem方案会出现sprite图定位不准确。至于设置一个div之类的宽高没有什么错位的

banrikun commented 9 years ago

@qc-zhan 我已经搞定啦,设置gulp.spritesmith的模板就可以了,公式如下:

{{#sprites}}
.icon-{{name}}:before {
    background-position: 0 {{offset_y}}/({{height}}-{{total_height}})*100%;
}
{{/sprites}}

地址:https://github.com/banrikun/webLog/issues/1

Medie commented 9 years ago

Hi I am not interested to get emails from regarding your projects

Thanks

On Mon, Sep 14, 2015 at 7:03 PM, br notifications@github.com wrote:

@qc-zhan https://github.com/qc-zhan 我已经搞定啦,设置gulp.spritesmith的模板就可以了,公式如下:

{{#sprites}} .icon-{{name}}:before { background-position: 0 {{offset_y}}/({{height}}-{{total_height}})*100%; } {{/sprites}}

— Reply to this email directly or view it on GitHub https://github.com/imweb/mobile/issues/3#issuecomment-140145044.

qc-zhan commented 9 years ago

@banrikun 简直太赞,看了你的文章了,学习了。目前完美解决这个问题,但目前只在几台手机测试过。 之前我是用dpr控制background-size 和 background-position,虽然不会有错位问题,但不是用rem就无法做到自适应了,而且图标量一多,生成的css代码量就很大,根据dpr方法编译后的代码大概如下。

a {
  display: block;
  width: 28px;
  height: 28px;
  background-image: url('../img/icons-s1fc22bf1d4.png');
  background-repeat: no-repeat;
  background-position-y: 0;
  background-position-x: -575px;
  background-size: 2053px;
}
[data-dpr="2"] a {
  width: 56px;
  height: 56px;
  background-size: 4106px;
  background-position-x: -1150px;
}
[data-dpr="3"] a {
  width: 84px;
  height: 84px;
  background-position-x: -1725px;
  background-size: 6159px;
}

看了确实蛋疼。

luckymore commented 9 years ago

1080*1920 请问我怎么判断。。我这图属于什么范围内?html [font-size] 应该是多少? 谢谢!

herbertliu commented 9 years ago

@luckymore font-size是根据你自己的页面来,没有固定的值。一般按经验来算,采用1rem=10px(即html [font-size]为10px),比较好计算,宽度为:1080/10=108rem。不过这样一来,每个rem数字较大,这时采用1rem=40px(即html [font-size]为40px)。那么宽度为:1080/40=27rem,也是OK的。

luckymore commented 9 years ago

@herbertliu

河伯,我没表达清楚。。。 html的[font-size]是js根据手机的大小决定的,比如说iPhone6 375 × 627,js计算出来的值是这样的:

<html lang="en" data-dpr="2" style="font-size: 75px;">

现在我不理解的是,我从拿到一个1080 × 1920设计稿开始,为它选择一个font-size基 ‘ s’ 来计算: 设计稿里有个300 × 100 的按钮,则:

.btn {
  width: (300/s)rem;
  heigth: (100/s)rem;
}

这其中的’s‘值是怎么确定的。。。有点白痴的问题呢😄

herbertliu commented 9 years ago

@luckymore 你说的是用那个js设置吧。首先根据iPhone6 375 × 627, image dpr为2,然后页面实际像素宽度 image 这里采用的dpr倍数是:10,所以宽度为计算出来的宽度是75px

那么,实际开发中。从上面的分析可以看到,倍数是10,,针对1080 × 1920。1080->750(iphone6为例)->10rem;所以这里1rem=108px(这里指的是视觉稿1rem对应的108px)。因此针对,300 × 100的按钮。对应的rem为300/108=2.78rem。

另外,我们采用的是倍数是16,即:1rem=46.875px(iphone6为例),视觉稿中:1rem=67.5px。

luckymore commented 9 years ago

@herbertliu

谢谢啦

小弟比较愚钝,,还请河伯不要烦我。。

ronyland commented 9 years ago

说得很好

majiang666 commented 8 years ago

已经解决,应用这个js头部的它是需要去掉的,js会自动添加 <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">

demo:http://www.love85g.com/code/

p { font-size: 12px; } /* dpr = 1 */

下边两句为什么在我测试页面不起作用,求解 [data-dpr="2"] p { font-size: 24px; } /* dpr = 2 / [data-dpr="3"] p { font-size: 36px; } / dpr = 3 */

herbertliu commented 8 years ago

@majiang666 效果是什么样子?有测试路径吗?

majiang666 commented 8 years ago

@herbertliu demo:http://www.love85g.com/code/

git-lt commented 8 years ago

开发时,假如视觉稿宽度是640,则最好使用第二种方案,选择16作为因子,则比例为640/16=40。那么,页面所有的rem数值的换算公式为:在视觉稿中的px数值/40

这里为什么要使用16做为因子? 如果是640下设计稿,根字体计算出来为100,这样不是更好计算rem的大小吗?

herbertliu commented 8 years ago

@git-lt 其实这里的用多少没关系的,而且计算这个可以通过插件来直接支持,开发仍然只需关注px,查看fis-parser-rem

suming1016 commented 8 years ago

html 后面那个font-size可以动态取值的; 图片自适应怎么办?根据dpr设计不同的图吗,有没有什么好方法()

suming1016 commented 8 years ago

@herbertliu 链接打不开404

herbertliu commented 8 years ago

@suming1016 OK了

SKing7 commented 6 years ago

没看到border怎么适配啊