「前端」rem缩放方案flexible-js兼容375px方案的思路

本文来自尚妆前端团队南洋

发表于尚妆github博客,欢迎订阅。

移动端H5页面rem缩放方案flexible.js兼容375px方案的思路

参考:

移动端高清、多屏适配方案

viewport-and-flexible.js

flexible.js github

一个新的项目复用了一些老页面,老页面是使用375px方案进行移动端适配的,meta[viewport]使用的是<meta name="viewport" content="width=375, user-scalabe=no">,而新页面使用的是flexible.js伸缩方案,动态生成meta[viewport]<meta name="viewport" content="initial-scale=[num], user-scalabe=no">
如何在老页面使用px布局的前提下,新页面使用rem布局,组件也使用rem布局,并且组件可以兼容老页面和新页面是本文的结果。
首先会介绍375px方案和rem方案的实现原理。

375px方案

<meta name="viewport" content="width=375, user-scalabe=no">
    375px方案的页面开发过程对新人非常的友好,利用页面的布局视口(layout viewport)为固定值375px,和移动端浏览器窗口的自动缩放功能(视觉视口==布局视口),可以很好的在大部分移动设备上展示375px宽度的内容。

    具体的开发前提是设计师给到一份750px宽的设计稿,前端同学根据ps量的像素的50%进行css书写。若一个banner宽度量的为750px,在css中编写为`width: 375px`。至于为什么是2倍的设计稿,可以参考[移动端高清、多屏适配方案](http://www.html-js.com/article/Mobile-terminal-H5-mobile-terminal-HD-multi-screen-adaptation-scheme%203041)这篇文章,可以找到答案。

    375px方案相对于把meta[viewport]中的width属性设置成`device-width`,然后通过媒体查询写几套css规则来说已经是非常方便了。把所有不同屏幕尺寸的手机的布局视口(layout viewport)设置成一个固定的值375px,无需考虑其他屏幕尺寸的情况。

    但375px方案的实现原理在某些安卓原生浏览器上有兼容问题,会产生一个重要bug --- 在某些安卓原生浏览器或webview中会出现视觉视口小于布局视口的情况。直观的显示就是页面会出现左右滑动。如下图:
375bug

而在上文也提到了375px方案得以实现就是依靠浏览器的原生能力 --- 迫使视觉视口等于布局视口。我们将这种情况下的document.documentElement.clientWidth(布局视口)window.innerWidth(视觉视口)打印看看。


浏览器的缩放效果没有实现,至于为什么,先看两条关于缩放的总结公式/经验。

一、meta标签内没有设置initial-scale的情况

浏览器计算出的缩放值 = layout viewport width(布局视口) / ideal viewport width(理想视口)

visual viewport width(视觉视口) = 浏览器计算出的缩放值 * ideal viewport width (理想视口)
===》
layout viewport width === visual viewport width // true

经过上述计算会将视觉视口会等于布局视口,布局上的所有内容都会出现在手机屏幕上。出现之前提到的bug的问题出在计算视觉视口上,浏览器会将所有计算出的缩放值都默认等于1,所以不管我们将布局视口设置能375还是其他任意值,视觉视口永远会是1 * ideal viewport width (理想视口)。ps:此款安卓机型的理想视口等于360.

二、meta标签内设置了initial-scale的值的情况

visual viewport width(视觉视口) = initial-scale(meta 标签内设置的初始缩放值) * ideal viewport width(理想视口又称设备独立像素)

layout viewport width = visual viewport width

解释:第二条总结经验正是rem伸缩方案flexiblejs的核心思想,设置了initial-scale后浏览器会计算出视觉视口,继而将布局视口的值自动设置成视觉视口的值。达到在屏幕上完整呈现布局上的内容。

但是在同样的安卓原生浏览器上,不管我们将initial-scale设置成多少,浏览器都默认值为1。所以视觉视口和布局视口永远都等于1 * ideal viewport width这个问题的hack办法在flexible.js里也有所体现。

375px方案就解释到这里,至于为何是375而不是其他的值比如360、320等,可以参考移动端高清、多屏适配方案以及viewport-and-flexible.js, 在后篇文章中也有介绍3种视口的一些概念。

rem方案

rem方案的目标也是用一套相同的度量标准适配所有屏幕大小的移动设备,在不同屏下进行正确的缩放。假设10rem宽在iphone5上是屏幕宽的一半,那么10rem在iphone6、iphone6plus、三星note等等机型上都显示为屏幕宽的一半。

我们知道rem所对应的px值是基于html标签上的font-size值进行换算的。若

html {
  font-size: 20px;
}

10rem === 200px //true

为了适配缩放所有设备,就要写个脚本动态设置html的fontSize值。同时要对页面的布局视口(layout viewport)和设计稿做一个划分约定,这里就约定这个值为10。(理论上可以任何值)

由以上两幅图可以知道,设计稿的一个区块对应1rem,布局视口的一个区块也对应1rem。而每个机型的布局视口该如何确定,flexible.js利用了上面提到的公式:

visual viewport width(视觉视口) = initial-scale(meta 标签内设置的初始缩放值) * ideal viewport width(理想视口又称设备独立像素)

浏览器自动将 layout viewport width = visual viewport width

之前也提到了initial-scale不为1的情况下部分安卓机型有bug,所以这里可以将initial-scale规定设置成1。

拿iphone6为例:理想视口为375px,经计算布局视口和视觉视口都等于375,html的fontSize等于37.5px。若设计稿量的是10个区块大小,那么在编写css时就写10rem,对应的width等于37。5 * 10 = 375px正好布满整个布局视口。

在flexible.js中对iphone设备的initial-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; // initial-scale

此处将initial-scale根据dpr动态设置是为了解决retina屏下border: 1px问题。而将安卓设备的dpr全部设置成1就是hack了之前提到的initial-scale在部分安卓机子上只能为1的bug。

回到border: 1px问题,有些设计师在750px的设计稿中设计了一条1px的边框线,而这个1px的边框线在各机型上最好的效果实际是占设备物理像素的1px。

拿iphone5和初代iphone为例:若设置initial-scale=1,那么布局视口为320px,但是iphone5的物理像素宽为640px,那就代表了1个css像素包含了4个物理像素(2x2)。而初代iphone的布局视口和物理像素都为320px如图:

在两者手机上显示的效果1px是一模一样的,但是iphone5包含着4倍的物理像素,就取上下高度而言,在iphone5上只用编写border: 0.5px就可以了。

但是部分机型对于0.5是不识别的,会直接赋值0。而我们想要做的就是令border的宽等于1个物理像素。我们可以这样做,根据window.devicePixelRatio属性动态缩放layout viewport,使之与屏幕的物理像素相同。这样我们只用在css里编写border: 1px,对应的就是物理像素的1px,border:1问题完美解决。

两种方案如何兼容

首先要考虑到组件也用rem进行布局,并且组件要在px布局和rem布局中都能兼容。那么就要全局head引入flexible.js的js脚本。

在375px布局方案里meta[viewport]已经设置,那么在flexible脚本中就要进行判断,已经设置viewport的就沿用,不动态创建meta[viewport]。

组件的rem布局要依赖html标签的font-size值,在新页面rem布局中已经实现。而在375px布局中,flexiblejs根据固定layout viewport值-- 375进行计算,那么所有屏幕尺寸下的页面html标签font-size值都为37.5px

还有一个问题,在375px布局中,全局css环境中设置了

html, body {
  font-size: 100%
}

而所有的浏览器实现默认字体大小为16px,所以在老页面中有些字没有设置大小,默认是16px,引入了flexible后html上的font-size为37.5px,body标签上的字体大小就会变成37.5 * 100% = 37.5px,而没有设置字体大小的字体就会变成37.5px,需要在flexible.js中设置针对这种情况。

if (doc.readyState === 'complete') {
        doc.body.style.fontSize = 16 + 'px';
    } else {
        doc.addEventListener('DOMContentLoaded', function(e) {
            doc.body.style.fontSize = 16 + 'px';
        }, false);
    }

最后的结果就是:

  • 全局引入flexible.js文件。
  • 375px布局的老页面上html标签的font-size固定为37.5px。body上的font-size固定为16px。
  • rem布局的新页面上html标签的font-size随不同机型而不同。
  • 组件编写一律按照rem布局,设计稿为750px,兼容新老页面。

原创文章,转载需谨慎 ~~

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

推荐阅读更多精彩内容

  • 在移动互联网快速发展的今天,手机的种类和尺寸越来越多,作为前端的小伙伴们可能会越来越头疼,但又不得不去适配一款又一...
    keenjaan阅读 26,784评论 9 86
  • 原文地址 在移动设备上进行网页的重构或开发,首先得搞明白的就是移动设备上的viewport了,只有明白了viewp...
    matthewm阅读 1,549评论 0 4
  • 偶然间发现这篇文章,可以说,这是我见到的对移动rem单位做的最详细,最细心的一篇总结性文章,果断转载至本博客,以便...
    阿宝er阅读 1,148评论 5 51
  • 好汉,介于英雄和小人之间,林冲。 林冲的一生都是被害的,从客观的角度来看林冲的一生都是被动的。梁山...
    Amy涵儿阅读 300评论 0 1
  • 2017年1月21日 @NYC 感冒初愈,晚上去看了一场节目,虽是因为工作,但也意外获得了一个灵感,让人有点小兴奋...
    Tess同学阅读 161评论 0 0