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

发表于尚妆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倍的设计稿,可以参考移动端高清、多屏适配方案这篇文章,可以找到答案。

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,兼容新老页面。

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


本文对你有帮助?欢迎扫码加入前端学习小组微信群:

「前端」rem 缩放方案 flexible-js 兼容 375px 方案的思路的更多相关文章

  1. 手淘移动适配方案flexible.js兼容bug处理

    什么是flexible.js 移动端自适应方案 https://www.jianshu.com/p/04efb4a1d2f8 什么是rem 这个单位代表根元素的 font-size 大小(例如 元素的 ...

  2. 移动端适配方案 flexible.js

    前言 移动端适配一直以来都是前端开发中不可或缺的重要组成部分,如果没有了它,那么你做出来的页面极有可能会出现各种意外(写出来的页面与设计稿之间的差别).所有我们得找到一种相对来说让人比较满意的解决方案 ...

  3. vue 的rem 配置和flexible.js的应用

    1.环境的配置: C:\Windows\System32\drivers\etc 2.多行注释: 光标放在函数中间,连按两次:“Ctrl+Alt+D”,就会出现以下的内容 3.总结使用过程:vue移动 ...

  4. 「前端」尚妆 UI 组件库工程实践(weex vue)

    本文来自尚妆前端团队南洋 发表于尚妆github博客,欢迎订阅! 前言 尚妆大前端团队使用 weex 进行三端统一开发有一段时间了,截止本文发表「达人店」APP大部分页面都已经用 weex 进行了重构 ...

  5. rem布局,flexible.js

    //author:caibaojian //website:http://caibaojian.com //weibo:http:weibo.com/kujian //这段js的最后面有两个参数记得要 ...

  6. rem适配布局(rem+less+媒体查询 和 rem+flexible.js)

    1. rem 基础 rem 是一个相对单位,类似于 em ,em 是父元素字体大小. em 是相对于父元素  的字体大小来说的 rem 是相对于 html 元素 字体大小来说的 rem 优点 就是可以 ...

  7. 一个「学渣」从零开始的Web前端自学之路

    从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...

  8. flexible.js 移动端自适应方案

    一,flexible.js 的使用方式: github地址:https://github.com/amfe/lib-flexible 官方文档地址:https://github.com/amfe/ar ...

  9. 有了这套flexible.js 移动端自适应方案,你就能在移动端的来去自如, (*^__^*)

    flexible.js 移动端自适应方案 一,flexible.js 的使用方式: github地址:https://github.com/amfe/lib-flexible 官方文档地址:https ...

随机推荐

  1. 吴裕雄--天生自然C语言开发:共同体

    union [union tag] { member definition; member definition; ... member definition; } [one or more unio ...

  2. c# winForm 将窗体状态栏StatusStrip 分成左中右三部分 右边显示当前时间

    实现效果:通过StatusStrip显示窗体状态栏同时将状态栏分成三部分居左边显示相关文字信息中间空白显示居右边显示时间信息 1.创建窗体及添加StatusStrip  默认StatusStrip名称 ...

  3. RDD(七)——依赖

    概述 RDD只支持粗粒度转换,即在大量记录上执行的单个操作.将创建RDD的一系列Lineage(血统)记录下来,以便恢复丢失的分区.RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD ...

  4. VS制作dll、def文件的使用、dll加入工程使用

    1.VS新建工程,在选项的时候,选择dll和空项目,保持干净的dll库: 创建完以后,添加头文件以及源文件. 2.将外部模块使用的接口导出: (1)函数导出: __declspec(dllexport ...

  5. aclocal-1.13: command not found

    原因: 将编译好的工程拷贝到系统版本不一样的系统中,再进行编译会出现此类问题. 解决方法: yum install automake autoconf yum install libtool auto ...

  6. Smarty使用-模版中编写js

      在smarty模版中编写js使用literal标签, Literal 标签区域内的数据将被当作文本处理,此时模板将忽略其内部的所有字符信息. 该特性用于显示有可能包含大括号等字符信息的 javas ...

  7. jQ给下拉框绑定事件,为什么要绑定在框(select标签)上,而不是绑定在选项(option标签)上

    这是我在学习锋利的 jquery 书中 5.1.4 的代码时遇到的一个小问题,源代码如下: <head> <style type="text/css"> * ...

  8. python语法基础-基础-变量和数据类型

    ###############   第一个python程序   ############### print("hello python") # 打印hello python # 分 ...

  9. HDU-6707-Shuffle Card(很数据结构的一道题)

    题目传送门 sol1:拿到这题的时候刚上完课,讲的是指针.所以一下子就联想到了双向链表.链表可以解决卡片移动的问题,但是无法快速定位要移动的卡片,所以再开一个指针数组,结合数组下标访问的特性快速定位到 ...

  10. Java中的注意点

    1.源文件以.java结束,源文件的基本组成部分是类(class) 2.每个源文件只能有一个public类,源文件名必须和该类的类名一致 3.每个Java程序的执行入口都是main()方法,固定写法为 ...