H5-WebApp 自适应方案 - rem

H5-WebApp 自适应方案 - rem

对于WebApp来说,为了更通用地满足各机型屏幕的自适应布局要求,我们目前采用rem布局方案。

rem

rem是相对于根元素(html)字体大小的单位,它只是一种相对单位。不同于另一个相对单位em,em是相对于父元素的字体大小,而rem则相对于根元素(html),与父元素的字体大小无关。

等比例适配所有屏幕

不论传统的px绝对像素布局,还是流式布局、固定宽度和响应式做法,都有其缺陷,并不能完全做到自适应所有屏幕。

但是,rem方案可以比较容易地做到等比例适配所有屏幕,保证各屏幕的显示效果与原始设计稿一致。

我们看看rem是如何工作的。

举个例子:(图片来自网络)

html{

font-size: 20px;

}

.btn {

width: 6rem;

height: 3rem;

line-height: 3rem;

font-size: 1.2rem;

display: inline-block;

background: #06c;

color: #fff;

border-radius: .5rem;

text-decoration: none;

text-align: center;

}

上面代码结果如下图:

如果我们把html的font-size改改,再看看效果。

html{

font-size: 40px;

}

可以看到,按钮的width、height、font-size和border-radius都被放大了一倍,我们只需要改变html的font-size,就能改变按钮在页面上的显示大小,而不必再去重设按钮的样式规则。

按钮的大小 = 按钮的rem * html.font-size

于是,利用这个特性,我们可以这样实现等比例适配所有屏幕。

基准屏幕宽度

以设计稿宽度作为最理想的基准屏幕宽度,假设设计稿宽度是750px,那么基准屏幕宽度就是750px,宽度大于750px的屏幕,等同于等比例放大了页面,小于750px的屏幕,等同于等比例缩小了页面。

把设计稿750px十等分一下,每等分=75px,我们可以把这个75px当做1个rem单位,那么750px宽度就等于是10rem。

设置html的font-size: 75px,即1rem,也就是rem的基准px=75px。

设计稿上的元素尺寸换算公式:原始px值 / rem基准px;例如240px * 120px的元素,其实就是3.2rem * 1.6rem。

适配任意屏幕

我们可以通过js来取得当前机型屏幕的宽度值,然后10等分得出当前屏幕1个rem应该代表的绝对像素值,再将这个rem基准px动态设置到html.font-size,核心代码如下:

var docEl = document.documentElement;

var width = docEl.getBoundingClientRect().width;

var rem = width / 10;

docEl.style.fontSize = rem + 'px';

我们还可以通过less预处理工具,编写一个绝对px转rem的函数,自动转换px值,省却我们手动计算rem的麻烦,核心代码如下:

@design-width: 750px; // 设计稿宽度

@rem: @design-width / 10; // 10等分得到的rem基准px

.px2rem(@attr; @px) when (ispixel(@px)) {

@{attr}: unit(@px / @rem, rem);

}

// 处理非px值

.px2rem(@attr; @px) when (default()) {

@{attr}: @px;

}

.px2rem(@attr; @px1; @px2) when (ispixel(@px1)) and (ispixel(@px2)) {

@{attr}: unit(@px1 / @rem, rem) unit(@px2 / @rem, rem);

}

.px2rem(@attr; @px1; @px2) when (ispixel(@px1)) and not (ispixel(@px2)) {

@{attr}: unit(@px1 / @rem, rem) @px2;

}

.px2rem(@attr; @px1; @px2) when not (ispixel(@px1)) and (ispixel(@px2)) {

@{attr}: @px1 unit(@px2 / @rem, rem);

}

// 处理非px值

.px2rem(@attr; @px1; @px2) when (default()) {

@{attr}: @px1 @px2;

}

然后用法如:

.px2rem(width; 240px);

.px2rem(padding; 10px; 20px);

对于Retina高清屏幕的处理

对于2倍和3倍的高清屏幕,统一做2倍处理。

处理方法是:重设viewport,将viewport缩小一倍。

核心代码如:

var dpr = 1;

var scale = 1;

if (window.devicePixelRatio >= 2) {

scale *= 0.5; // viewport缩小一倍

dpr *= 2;

}

docEl.setAttribute('data-dpr', dpr);

metaEl.setAttribute('content', 'initial-scale=' + scale + ', width=device-width, maximum-scale=' + scale + ', user-scalable=no');

字号不用rem

字号大小不推荐用rem作为单位,否则可能会有文字排版问题。因此,字号仍旧使用px作为单位,并配合data-dpr自定义属性来在普通屏和高清屏设置不同的font-size。

高清屏的font-size=设计稿的font-size,普通屏是设计稿font-size的一半。

处理方式是:设定body的font-size,此后页面上所有元素的字号大小都是相对于body的font-size,而不是html的font-size。

核心代码如:

var fontBase = 16; // 普通屏基准字号:16px,高清屏基准字号:16px * 2

if (doc.readyState === 'complete') {

document.body.style.fontSize = fontBase * dpr + 'px';

} else {

document.addEventListener('DOMContentLoaded', function(e) {

document.body.style.fontSize = fontBase * dpr + 'px';

}, false);

}

// 默认普通屏,设计稿字号的一半

h3 {

font-size: 18px;

}

// 高清屏:设计稿的字号

[data-dpr="2"] h3 {

font-size: 36px;

}

完整代码

var FS = {};

(function(win, FS) {

var doc = win.document;

var docEl = doc.documentElement;

var metaEl = doc.querySelector('meta[name="viewport"]');

var dpr = 1;

var scale = 1;

var fontBase = 16;

if (window.devicePixelRatio >= 2) {

scale *= 0.5;

dpr *= 2;

}

docEl.setAttribute('data-dpr', dpr);

metaEl.setAttribute('content', 'initial-scale=' + scale + ', width=device-width, maximum-scale=' + scale + ', user-scalable=no');

function refreshRem() {

var width = docEl.getBoundingClientRect().width;

var rem = width / 10;

docEl.style.fontSize = rem + 'px';

FS.rem = rem;

}

var tid = null;

win.addEventListener('resize', function() {

clearTimeout(tid);

tid = setTimeout(refreshRem, 300);

}, false);

if (doc.readyState === 'complete') {

doc.body.style.fontSize = fontBase * dpr + 'px';

} else {

doc.addEventListener('DOMContentLoaded', function(e) {

doc.body.style.fontSize = fontBase * dpr + 'px';

}, false);

}

refreshRem();

FS.dpr = dpr;

FS.rem2px = function(d) {

var val = parseFloat(d) * FS.rem;

if (typeof d === 'string' && d.match(/rem$/)) {

val += 'px';

}

return val;

}

FS.px2rem = function(d) {

var val = parseFloat(d) / FS.rem;

if (typeof d === 'string' && d.match(/px$/)) {

val += 'rem';

}

return val;

}

})(window, FS);

From Fxiaoke

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

推荐阅读更多精彩内容