移动端布局方案浅谈
July 31, 2017/ Edit on Github ✏️一直想抽时间总结一下现在业界可用的移动端布局方案。需求多,995 模式,身心被掏空,一拖再拖,终于在周六的晚上,做着梦弄完成的,不足之道 😪,请指教改正 🤠。
media+百分比
直接采用媒体查询加百分比去布局。
核心思想:
- 页面主体布局基本采用百分比去设置。
- 然后,其他块内布局,例如 font-size 等可通过设置媒体查询的断点来分设置,已达到兼容其他手机尺寸。
优点:
- 灵活,简单
- 兼容性好,媒体查询,97.65%的浏览器都支持了。
缺点:
- 媒体查询的断点设置麻烦,只能在选取的几个主流设备尺寸下呈现完美适配。
- 1px 的问题,需要额外做处理,比如 transform:scaleX(0.5)。
hotcss
这个方案比较简单和实用,理解起来也不难。项目地址查看这里
原理:
- 自动通过 hotcss.js 计算页面的
html
的font-size
的大小,其后在布局中使用 rem 作为像素单位,使得页面在不同的移动设备上可以视觉一致。 - 通过获取 dpr,在
html
上设置属性data-dpr
,在设置font-size
时,可以配合data-dpr
来使用 px 作为像素单位。 - 计算 scale,为页面设置
viewport
,使得页面根据 dpr 进行缩放,最终达到,1 个物理像素=1px。这样,也有效解决了 1px 的问题。 - 默认在 320px 屏幕中(非 Retina 屏),1rem=20px;
核心源码:
计算 dpr 并设置data-dpr
和scale
// 计算dpr
var dpr = window.devicePixelRatio || 1
// 在其他的一些方案中,会区分ios和android,对android统一设置为1.
dpr = dpr >= 3 ? 3 : dpr >= 2 ? 2 : 1
// 设置data-dpr
document.documentElement.setAttribute("data-dpr", dpr)
// 计算scale
var scale = 1 / dpr,
content =
"width=device-width,initial-scale=" +
scale +
",minimum-scale=" +
scale +
",maximum-scale=" +
scale +
",user-scalable=no"
// 插入meta,设置页面缩放
var viewport = document.createElement("meta")
viewport.setAttribute("name", "viewport")
viewport.setAttribute("content", content)
document.head.appendChild(viewport)
计算font-size
并设置html
的font-size
// 获取设备的宽度
var innerWidth =
document.documentElement.getBoundingClientRect().width || window.innerWidth
// 计算font-seize,默认320px中,1rem=20px.
document.documentElement.style.fontSize = (innerWidth * 20) / 320 + "px"
优点:
- 后续布局处理简单,配合 px2rem 插件等,使用十分方便。
- 基本能很好适配市面即大多数尺寸手机。
- 兼容性也比较好,97.62%浏览器基本支持 rem 单位了。
- 通过设置缩放比例,有效解决了 1px 的问题。
缺点:
- 页面需要额外引入 js 脚本去计算,增加一次额外的 http 请求,或内嵌到页面中增加页面体积大小。
flexible
这个是手机淘宝推出来一个方案,解决手机淘宝 H5 在移动端的布局。项目地址查看这里
原理:
- 自动通过 flexible.js 计算页面
html
的font-size
的大小,然后在页面布局中使用 rem 作为单位,而非 px。 - 如果页面指定了
meta[name="viewport"]
,则直接根据已有的 meta 标签来设置缩放比例;否则,通过dpr
来自动计算页面缩放比例。 - 设置 1rem=10vw,例如,在 320px 屏幕(非 Retina 屏),1rem=32px;
核心源码:
计算缩放比例
// 参试获取页面已定义的缩放比例
var metaEl = doc.querySelector('meta[name="viewport"]')
if (metaEl) {
console.warn("将根据已有的meta标签来设置缩放比例")
var match = metaEl.getAttribute("content").match(/initial\-scale=([\d\.]+)/)
if (match) {
scale = parseFloat(match[1])
dpr = parseInt(1 / scale)
}
}
// 如果没有,则自动计算
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
}
设置 meta
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)
}
}
计算页面的 html 的 font-size
var width = docEl.getBoundingClientRect().width
// 最大屏幕宽度为540*dpr
if (width / dpr > 540) {
width = 540 * dpr
}
// 设置1rem=10vw
var rem = width / 10
docEl.style.fontSize = rem + "px"
flexible.rem = win.rem = rem
优缺点同 hotcss。
rem+vw
这种方案就是在计算 html 的 font-size 时,是根据 vw 去自动计算,而不是用 js 脚本去动态计算。vw 是一种新的单位,类似的还有 vh,vmin,vmax。定义如下:
- vw:1vw 等于视口宽度的 1%;例如,320px 宽度视口中,1vw=3.2px,10vw=32px,100vw=320px;
- vh:1vh 等于视口高度的 1%;例如,568px 高度视口中,1vh=5.58px,10vh=56.8px,100vh=568px;
- vmin:选取 vw 和 vh 中最小的那个;
- vmax:选取 vw 和 vh 中最大的那个;
核心思想:
不用 js 去动态计算页面的 font-size ,而是设置根元素 html 的 font-size=a vw;这里的a,可以选取一种屏幕尺寸去参考计算。例如,我们想 iPhone6 下,html 的 font-size 为 100px。那个 html 的 font-size=100/375=0.26666666666666666vw。那么,其他屏幕的适配计算方式就是:
iphone5:屏幕是320*568
html的font-size=0.26666666666666666*320=85.33333333333333
iphone6s:屏幕是414*736
html的font-size=0.26666666666666666*414=110.39999999999999
页面布局采用 rem 布局,而非 px。根据 html 的 font-size 和 rem 单位自动计算出了布局 px。
优点:
- 不需要额外引入 js 脚本去计算,自动根据 vw 去计算 font-size
- 后续布局也超级简单,采用 rem 即可。
缺点:
- vw 单位的支持相对比 rem 的浏览器少一些,目前有 93.65%的浏览器都支持了。
若有收获,小额鼓励