盘点移动端适配方案
作为开发者,在手机移动端做适配的时候会出现很多问题:最不希望用户看到的就是横向滚动条。其次是文字(图片等)的大小不能一成不变,要根据用户设备的物理像素调节大小。
手机浏览器是把页面放在一个虚拟的"窗口"(viewport)中,通常这个虚拟的"窗口"(viewport)比屏幕宽,这样就不用把每个网页挤到很小的窗口中(<u>这样会破坏没有针对手机浏览器优化的网页的布局</u>),用户可以通过平移和缩放来看网页的不同部分。
这就该轮到meta标签出场了。meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放,不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这个应该是大家都想要的效果,如果你不这样的设定的话,就会使用那个比屏幕宽的默认viewport,也就是说会出现横向滚动条;
- 设置Viewport
一个常用的针对移动网页优化过的页面的 viewport meta 标签大致如下:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- width
控制 viewport 的大小,可以指定的一个值,如 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
- initial-scale
初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
- user-scalable
用户是否可以手动缩放(默认设置为no,因为我们不希望用户放大缩小页面)
- minimum-scale
允许用户缩放到的最小比例(默认设置为1.0)
- maximum-scale
允许用户缩放到的最大比例(默认设置为1.0)
一、rem适配
rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。
正是因为它适配的标准是根元素的字体大小,而不同的手机型号对于根元素的规定不同[1],这便增加了很多不必要的判断。
二、vw适配
vw(viewpoint width),即视窗宽度,1vw等于视窗宽度的1%
计算方式(以750的设计稿为准):设计图中元素固定的大小(px) * 100 / 750 => vw
eg : 90px转化为vw : 90 * 100 / 750 => 12vw
如果是375的设计稿需要乘以2 : 设计图中元素固定的大小(px) * 2 * 100 / 750 => vw
不难看出,使用的时候仍需进行大量的计算。
三、vw+rem适配
因为vw的比例需要动态地计算,而rem做移动端布局的时候刚好需要动态地改变,因此我们只要稍加计算,将根元素的字体大小换成vw即可。
-
640的设计稿
320px == 100vw ; 1px == 100 / 320 == 0.3125vw ; 100px == 31.25vw;
此时设置根元素字体大小
html,body{font-size:31.25vw}
, => 1rem == 100px; =>1px == 0.01rem;所以,根据设计稿的像素计算时,只需将px除以100即可
计算方式:元素尺寸 / 2 / 100 = rem
320的设计稿计算方式:元素尺寸 / 100 = rem
-
750的设计稿
375px == 100vw ; 1px == 100 / 375 == 0.26666vw ; 100px == 26.6666vw
出现了小数点后除不尽的情况……为了将结果它转换成整数 120px == 32vw此时设置根元素字体大小
html,body{font-size:32vw}
, 计算方式也发生改变:元素尺寸 / 2 / 120 = rem375的设计稿计算方式:元素尺寸 / 120 = rem
以上面的几种设计稿为例,尤其是750的设计稿居多,大多数情况下根据元素的尺寸 / 120 ,这么难以计算的数字,还是交给插件来做吧。以VScode中的cssrem为例:
注意:配置完参数之后,重启软件
四、flexible.js布局(推荐)
通过flexible.js实现了rem自适应,有了flexible.js,我们就不必再为移动端各种设备兼容烦恼。通过rem与px的换算,把设计稿从px转到rem。再也不用为各种设备横行而担忧。
rem是相对于根元素html,这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高。1rem=16px(浏览器html的像素,可以设定这个基准值),假如浏览器的html设为64px,则下面的元素则1rem=64px来运算。
阿里团队开源的一个库。使用flexible.js轻松搞定各种不同的移动端设备兼容自适应问题。
-
删除viewport的meta标签,替换为下面的JS代码
(function (win, lib) { var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.querySelector('meta[name="viewport"]'); var flexibleEl = doc.querySelector('meta[name="flexible"]'); var dpr = 0; var scale = 0; var tid; var flexible = lib.flexible || (lib.flexible = {}); 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) { var content = flexibleEl.getAttribute('content'); if (content) { var initialDpr = content.match(/initial\-dpr=([\d\.]+)/); var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/); if (initialDpr) { dpr = parseFloat(initialDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } if (maximumDpr) { dpr = parseFloat(maximumDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } } } if (!dpr && !scale) { var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案 if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) { dpr = 2; } else { dpr = 1; } } else { // 其他设备下,仍旧使用1倍的方案 dpr = 1; } scale = 1 / dpr; } docEl.setAttribute('data-dpr', dpr); if (!metaEl) { metaEl = doc.createElement('meta'); metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(metaEl); } else { var wrap = doc.createElement('div'); wrap.appendChild(metaEl); doc.write(wrap.innerHTML); } } function refreshRem() { var width = docEl.getBoundingClientRect().width; if (width / dpr > 768) { width = 768 * dpr; } var rem = width / 7.5; docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem; } win.addEventListener('resize', function () { clearTimeout(tid); tid = setTimeout(refreshRem, 300); }, false); win.addEventListener('pageshow', function (e) { if (e.persisted) { clearTimeout(tid); tid = setTimeout(refreshRem, 300); } }, false); if (doc.readyState === 'complete') { doc.body.style.fontSize = 12 * dpr + 'px'; } else { doc.addEventListener('DOMContentLoaded', function (e) { doc.body.style.fontSize = 12 * dpr + 'px'; }, false); } refreshRem(); flexible.dpr = win.dpr = dpr; flexible.refreshRem = refreshRem; flexible.rem2px = function (d) { var val = parseFloat(d) * this.rem; if (typeof d === 'string' && d.match(/rem$/)) { val += 'px'; } return val; } flexible.px2rem = function (d) { var val = parseFloat(d) / this.rem; if (typeof d === 'string' && d.match(/px$/)) { val += 'rem'; } return val; } })(window, window['lib'] || (window['lib'] = {}));
以750设计稿为准,元素尺寸 / 100 = rem ; 以375设计稿为准,元素尺寸 * 2 / 100 = rem ,因为flexble中会再除以2,所以这里乘以2将其抵消。
-
根字体大小:iPhone5s:12px iPhone6S:14px iPhone6P:16px ↩