致谢
一直以来移动端适配便是困扰自己的一个难题, 今天休息, 查阅了很多资料将其整理出来本文中很多做法是直接借鉴了网上的文章, 主要有从网易与淘宝的 font-size 思考前端设计稿与工作流跟手机端页面自适应解决方案 rem 布局进阶版(附源码示例), 向大神致谢
移动设备的分辨率
设备 | 屏幕尺寸 | 逻辑分辨率 (pt) | Reader | 物理分辨率 (px) |
---|---|---|---|---|
iPhone 3GS | 3.5 寸 | 320*480 | @1x | 320*480 |
iPhone 4/4S | 3.5 寸 | 320*480 | @2x | 640*960 |
iPhone 5/5S/5C | 4.0 寸 | 320*568 | @2x | 640*1136 |
iPhone 6/6S | 4.7 寸 | 375*667 | @2x | 750*1134 |
iPhone 6/6S Plus | 5.5 寸 | 414*736 | @3x | 1242*2208 |
pt 是逻辑分辨率, 简单理解就是跟屏幕的尺寸有关系, 是个长度跟视觉的单位
px 是物理分辨率, 简单理解为像素点, 跟屏幕的尺寸是没关系的
小结: pt 与 px 的关系是, 一个单位的 pt 里包含几个 px, 包含的 px 越多, 则越清晰但因为人视网膜的关系, 最多只能识别单位 pt 里 2 个像素点, 大于 2 个像素点, 人眼识别不出, 所以 6plus 看上去不会比 6 更清晰
移动端 web 开发的设计稿与工作流
移动端 web 开发的难点之一是适应各种分辨率的移动设备众所周知, 应该使用 rem
但实际操作中, 如何确定 html 上的 font-size, 是一个难点一般有以下几种方式:
使用 CSS3 媒体查询
- html{font-size:10px}
- @media screen and (min-width:321px) and (max-width:375px){html{font-size:11px}}
- @media screen and (min-width:376px) and (max-width:414px){html{font-size:12px}}
- @media screen and (min-width:415px) and (max-width:639px){html{font-size:15px}}
- @media screen and (min-width:640px) and (max-width:719px){html{font-size:20px}}
- @media screen and (min-width:720px) and (max-width:749px){html{font-size:22.5px}}
- @media screen and (min-width:750px) and (max-width:799px){html{font-size:23.5px}}
- @media screen and (min-width:800px){html{font-size:25px}}
用 CSS3 媒体查询明显不足有:
需要写大量的媒体查询以适应不同的设备
媒体查询的范围不一定合适
每个媒体查询里的 font-size 难以定义
每次给元素设置 rem 都需要根据某个分辨率 html 的 font-size 去算, 工作量大
小结: CSS3 媒体查询理论上可以, 但操作起来不灵活
简单问题简单解决
有些 web app 比较简单, 记住一个开发原则就好: 文字流式, 控件弹性, 图片等比缩放以图描述:
移动端布局 1.png
网易的做法
网易的页面复杂度较高, 随着分辨率增大, 网易页面的效果也会发生明显变化, 要达到这种效果, 就需要使用 rem 作为单位, 且 < font color="red">html 的 font-size 是通过 js 计算出来的</font>
网易移动 web 的工作流可以总结如下:
第一步 设置视口的 viewport
<meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
第二步 以 iphone6/6S 设计稿为基准确定 body 及页面元素的尺寸
网易网页的设计稿是基于 iphone6/6S, 物理分辨率 (设计稿宽度) 为 750px, 逻辑分辨率 (deviceWidth) 为 375
为了计算方便, 先拿设计稿竖着的横向分辨率除以 100 得到 body 元素的宽度: 750 / 100 = 7.5rem
同理, 布局时页面上的元素的尺寸, 可以拿设计图标注的尺寸除以 100 得到
第三步 计算 html 的 fontSize 以适配各个尺寸的屏幕
document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5 + 'px';
第四步 细节调整
当 deviceWidth 大于设计稿的横向分辨率时, html 的 font-size 始终等于横向分辨率 / body 元素宽
之所以这么干, 是因为当 deviceWidth 大于 640 时, 则物理分辨率大于 1280, 应该去访问 pc 网站了只需将第三步做下调整即可
- var deviceWidth = document.documentElement.clientWidth;
- if (deviceWidth > 640) deviceWidth = 640;
- document.documentElement.style.fontSize = deviceWidth / 6.4 + 'px';
可能需要额外的媒介查询
网页上有一些结构是不需要随着屏幕变大而相应调整, 比如底部导航栏, 此时可以使用媒体查询
淘宝的做法
知识预备, 了解 viewport
通常我们采用如下代码设置 viewport:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
这样整个网页在设备内显示时的页面宽度就会等于设备逻辑像素大小, 也就是 device-width 这个 device-width 的计算公式为:
设备的物理分辨率 / (devicePixelRatio * scale)
devicePixelRatio 称为设备像素比, 每款设备的 devicePixelRatio 都是已知, 并且不变的, 目前高清屏, 普遍都是 2, 不过还有更高的, 比如 2.5,3 等淘宝触屏版布局的前提就是 viewport 的 scale 根据 devicePixelRatio 动态设置.
在 devicePixelRatio 为 2 的时候, scale 为 0.5
在 devicePixelRatio 为 3 的时候, scale 为 0.3333
这么做目的是为了保证页面的大小与设计稿保持一致, 比如如果是 750 的横向物理分辨率, 那么实际页面的 device-width 也等于 750
接下来要解决的问题是:
元素的尺寸该如何计算, 比如说设计稿上某一个元素的宽为 150px, 换算成 rem 应该怎么算呢?
这个值等于设计稿标注尺寸 / 该设计稿对应的 html 的 font - size
拿淘宝来说的, 他们用的设计稿是 750 的, 所以 html 的 font-size 就是 75, 如果某个元素时 150px 的宽, 换算成 rem 就是 150 / 75 = 2rem
第一步 动态设置 viewport 的 scale
- var scale = 1 / devicePixelRatio;
- document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
第二步 动态计算 html 的 font-size
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
因为第一步已经根据 devicePixelRatio 动态设置 scale, 此时 device-width 便等于页面的横向物理分辨率(document.documentElement.clientWidth), 也就是设计稿的的横向物理分辨率
淘宝的做法是, 设计稿是 750, 那么 html 的 font-size 就是 75
第三步 确定布局时各个元素的尺寸
布局的时候, 各元素的 css 尺寸 = 设计稿标注尺寸 / 设计稿横向分辨率 / 10 = 设计稿标注尺寸 / html 的 font - size
第四步 细节调整
font-size 可能需要额外的媒介查询, 并且 font-size 不使用 rem, 这一点跟网易是一样的
跟网易一样, 淘宝也设置了一个临界点, 当设备竖着时横向物理分辨率大于 1080 时, html 的 font-size 就不会变化了, 原因也是一样的, 分辨率已经可以去访问电脑版页面了
与网易的做法比较
共同点:
都能适配所有的手机设备, 对于 pad, 网易与淘宝都会跳转到 pc 页面, 不再使用触屏版的页面
都需要动态设置 html 的 font-size
布局时各元素的尺寸值都是根据设计稿标注的尺寸计算出来, 由于 html 的 font-size 是动态调整的, 所以能够做到不同分辨率下页面布局呈现等比变化
容器元素的 font-size 都不用 rem, 需要额外地对 font-size 做媒介查询
都能应用于尺寸不同的设计稿, 只要按以上总结的方法去用就可以了
不同点
淘宝还需要动态设置 viewport 的 scale, 网易不用
网易的做法, rem 值很好计算, 淘宝的做法可能需要使用 less 和 sass 的 css 处理器
阿里团队的高清方案布局
高清方案的源码
- 'use strict';
- /**
- * @param {Boolean} [normal = false] - 默认开启页面压缩以使页面高清;
- * @param {Number} [baseFontSize = 100] - 基础 fontSize, 默认 100px;
- * @param {Number} [fontscale = 1] - 有的业务希望能放大一定比例的字体;
- */
- const win = window;
- export default win.flex = (normal, baseFontSize, fontscale) => {
- const _baseFontSize = baseFontSize || 100;
- const _fontscale = fontscale || 1;
- const doc = win.document;
- const ua = navigator.userAgent;
- const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
- const UCversion = ua.match(/U3\/((\d+|\.){5,})/i);
- const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80;
- const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
- let dpr = win.devicePixelRatio || 1;
- if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {
- // 如果非 iOS, 非 Android4.3 以上, 非 UC 内核, 就不执行高清, dpr 设为 1;
- dpr = 1;
- }
- const scale = normal ? 1 : 1 / dpr;
- let metaEl = doc.querySelector('meta[name="viewport"]');
- if (!metaEl) {
- metaEl = doc.createElement('meta');
- metaEl.setAttribute('name', 'viewport');
- doc.head.appendChild(metaEl);
- }
- metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`);
- doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * _fontscale}px`;
- };
原理
与上述淘宝的原理一致
动态设置 html 的 font-size, 同时根据设备 DPR 调整页面的缩放值, 此时 device-width 便等于页面的横向物理分辨率(document.documentElement.clientWidth), 也就是设计稿的的横向物理分辨率
优势
引用简单, 布局简便
根据设备屏幕的 DPR, 自动设置最合适的高清缩放
保证了不同设备下视觉体验的一致性(老方案是, 屏幕越大元素越大; 此方案是, 屏幕越大, 看的越多)
有效解决移动端真实 1px 问题(这里的 1px 是设备屏幕上的物理像素)
使用
此方案也是默认 1rem = 100px, 布局时, 根据设计稿尺寸 / 100 即可得到物理分辨率的尺寸
注意事项
如果元素的宽度超过效果图宽度的一半(效果图宽为 640 或 750), 果断使用百分比宽度, 或者 flex 布局可以避免类似于在 iphone6 上没问题, 在 iphone5 上会有横向滚动条的问题
此方案是根据设备的 dpr 动态设置 html 的 font-size, 且默认 DPR 是 2,1rem = 100px, 如果设计稿是 iphone 6 sp (dpr = 3), 将代码的最后的 flex(false, 100, 1)修改成
flex(false, 66.66667, 1)
如何与设计师协作
手机淘宝团队适配协作模式. png
视觉设计阶段, 设计师按宽度 750px(iPhone 6)做设计稿, 除图片外所有设计元素用矢量路径来做设计定稿后在 750px 的设计稿上做标注, 输出标注图同时等比放大 1.5 倍生成宽度 1125px 的设计稿, 在 1125px 的稿子里切图
输出两个交付物给开发工程师: 一个是程序用到的 @3x 切图资源, 另一个是宽度 750px 的设计标注图之所以要在 @3x 的图里切, 这是因为现在市面上也有不少像魅蓝 note 这种超高清屏幕, devicePixelRatio 已经达到 3 了, 这个切图保证在所有设备都清晰显示
开发工程师拿到 750px 标注图和 @3x 切图资源, 完成 iPhone6(375pt)的界面开发, 需要使用上文提到的适配方法
适配调试阶段, 基于 iPhone 6 的界面效果, 分别向上向下调试 iPhone 6 plus(414pt)和 iPhone 5S 及以下 (320pt) 的界面效果由此完成大中小三屏适配
来源: http://www.jianshu.com/p/638d66981804