换一种rem方式进行移动端布局
公司的移动端项目,包括app内的webview页面和Wap端网站,均采用rem进行布局,不过并不是基于flexible.js通过js去设置根字体大小,而是采用纯CSS实现的rem布局,在项目中的效果还不错,下面简单整理一下。
其实早前已经在github开了一个项目,这里只是把相关内容补充一下。
参考:
- 基于vw等viewport视区单位配合rem响应式排版和布局,整个实现的核心思想就是参考了张鑫旭大神的这篇博客
- Rem布局的原理解析
实现原理
rem布局的本质是等比缩放,其基本原理就是通过html根字体大小去设置布局尺寸的大小,不同的分辨率对应不同的根字体,这样就可以按比例还原设计图了。
当然这里我们讨论的不是rem布局的原理,而是如何基于CSS去实现rem布局,其实现十分简单。
$design-width: 750;
html {
font-size: calc((100vw / $design-width) * 100);
}
分别解释一下:
100vw
表示屏幕的宽度$design-width
表示设计图尺寸,项目中一般为750pxcalc
,CSS3新增的属性,通过计算来决定一个CSS属性值,参考MDN文档
上面的样式声明了html的字体大小(即rem值),其值为窗口大小 / 设计图尺寸 的100倍,之所以设置为100倍,是为了防止浏览器的最小字体限制导致的渲染错误。 仅仅通过上面这一段代码,就可以根据屏幕分辨率,自动计算rem大小,然后只需要在布局中采用rem作为尺寸单位,即可实现rem布局,是不是很神奇!
设计图尺寸
为什么要将rem大小与设计图尺寸相关联呢?这样处理的话,我们可以直接将设计图标注的尺寸编写到样式表中,而无需再次计算对应的换算关系。
可以通过下面这个scss函数将设计图尺寸转换为rem
@function rem($px) {
@if (unitless($px)) {
@return 1rem * ($px/100);
} @else {
@return 1rem * ($px/100px);
}
}
其中$px
参数即为设计图标注的尺寸,样式表中只需要编写如下代码即可
.box {
width:rem(200)
}
其中,200为750标准设计图的标注尺寸。这样基本可以完美还原设计图。甚至在微信小程序中的开发中,可以通过将rem单位修改为rpx(这个是小程序本身的rem实现单位),实现样式表的无缝迁移~
其实现在有相关的postCSS插件可以实现自动将px转换为rem单位,比如pxtorem。
但是在由于公司项目的特殊性(文字阅读),某些地方还是依赖于px进行单位设置,因此我更倾向于通过这个rem
函数来进行手动控制样式表。根据过去的项目经验,敲一个rem(200)
和200px
然后进行pxtorem的转换,工作成本其实是差不多的。
最大尺寸
使用rem布局的一个问题是在PC端打开的移动端页面有些奇怪,。因为PC的宽度比较大,因此计算得到的rem也会比较大,导致设计图放大到了一个不太合理的比例。
我的解决办法是给定一个页面宽度的最大值
@media screen and (min-width:800px) {
html {
font-size: 100px;
}
body {
width: 800px;
margin: 0 auto;
}
}
这样的话即使在很大的屏幕上,整个页面也能比较合理的展示。
但是会引入另外一个问题,即fixed的元素可能会溢出容器,在某些fixed
定位、宽度为100%
的DOM上,表现的就比较奇怪了,因为他们的包含块是窗口本身,处理方式是定义一个rem-fixed
的颗粒类
@media screen and (min-width:800px) {
.rem-fixed {
width: 800px;
left: 0;
right: 0;
margin-left: auto;
margin-rigth: auto;
}
}
在某些特定的布局上可能会有些问题,按需处理即可。之前张鑫旭大神在博客中提出了一个更合理的实践方案
html {
font-size: 16px;
}
@media screen and (min-width: 375px) {
html {
/* iPhone6的375px尺寸作为16px基准,414px正好18px大小, 600 20px */
font-size: calc(100% + 2 * (100vw - 375px) / 39);
font-size: calc(16px + 2 * (100vw - 375px) / 39);
}
}
@media screen and (min-width: 414px) {
html {
/* 414px-1000px每100像素宽字体增加1px(18px-22px) */
font-size: calc(112.5% + 4 * (100vw - 414px) / 586);
font-size: calc(18px + 4 * (100vw - 414px) / 586);
}
}
@media screen and (min-width: 600px) {
html {
/* 600px-1000px每100像素宽字体增加1px(20px-24px) */
font-size: calc(125% + 4 * (100vw - 600px) / 400);
font-size: calc(20px + 4 * (100vw - 600px) / 400);
}
}
@media screen and (min-width: 1000px) {
html {
/* 1000px往后是每100像素0.5px增加 */
font-size: calc(137.5% + 6 * (100vw - 1000px) / 1000);
font-size: calc(22px + 6 * (100vw - 1000px) / 1000);
}
}
这里的缺点在于:尺寸并不是完全按照设计图的比例还原,如果设计师那边没问题,这种处理方式更优,因为页面能在PC上也能铺满整个屏幕。
向后兼容
上面的实现完全由CSS控制,不需要根据JavaScript来动态计算根字体的大小。但是仍旧需要考虑calc兼容性的问题。这里是关于calc的兼容列表,可以看见基本满足生产条件了。
在需要适配某些不支持calc特性的版本下(比如在Android V4.4.4以下版本不支持乘法和除法计算),可以使用JavaScript向下兼容(虽然很不情愿这么干~)
通过js计算rem大小比较容易
var newRem = function() {
var html = document.documentElement
// 设计图标准750px
var baseWidth = 750
// 屏幕最大宽度800px
var screenWidth = Math.min(800, html.getBoundingClientRect().width)
html.style.fontSize = screenWidth / 750 * 100 + 'px';
};
我们只需要在不支持calc特性的浏览器上调用newRem
函数即可,下面是自己写的一个hack方法,暂时没找到更好的做法~
// 获取元素的计算样式
function getStyle(dom, key){
var style
if (window.getComputedStyle) {
style = window.getComputedStyle(dom, null)
} else {
style = dom.currentStyle // fuck IE
}
return style[key]
}
// 判断浏览器是否支持calc
function isSupportCalc(){
var oDiv = document.createElement("div");
oDiv.style.width = 'calc((100vw / 750) * 100)'
document.body.appendChild(oDiv)
var width = getStyle(oDiv, 'width')
oDiv.remove()
var diff = 0.5
width = parseFloat(width)
var calcWidth = window.screen.availWidth / 750 * 100
return width && Math.abs(calcWidth - width) < diff
}
// 判断浏览器calc特性
var isSupport = isSupportCalc()
if (!isSupport){
window.addEventListener('resize', newRem, false);
newRem()
}
OK,这样就解决了calc不兼容或vw不兼容导致的页面排版异常的情形。
小结
之所以采用这种方式实现rem,最主要的原因是:非常酷。我一直坚信着能不用JS实现的CSS效果,就不用CSS实现。
毫无疑问,基于calc和vw实现的rem布局,刚好满足这个条件。因此我义无反顾地在项目中使用了这种形式,当然,为了向后兼容,还是需要引入相关的polyfill的,毕竟技术是为了业务服务的,我们还是需要对项目和用户负责的哈哈。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。
