前言:rem是目前移动端常用的布局之一,此外还有何其相似的em,em一般代表父元素的字体大小,而rem是相对于根元素字体大小的值。

flexible.js是手机淘宝团队出的简洁高效移动端适配库,其GitHub地址在 https://github.com/amfe/lib-flexible

rem的原理

  • rem布局的本质是等比缩放,一般是基于屏幕宽度
  • 具体来说,rem 是指相对于文档根节点(即 HTML 标签)字号大小的单位。例如,如果文档根节点的字号大小为 16px,那么 1rem 就等于 16px。2rem就是32px

rem的使用

  • 可以使用 CSS 的 @media 和 JavaScript 等方式,根据设备屏幕宽度、像素密度等因素来动态设置字号大小,适合用于响应式设计,以适配不同设备的需求

rem的局限性和兼容性

  • 低版本浏览器支持问题:IE8 及以下的浏览器不支持使用 rem 单位,需要通过 polyfill 或者其他方式进行降级处理。
  • 超大分辨率问题:在极端情况下,可能出现因为可以配置的最大 rem 值受到了限制导致的布局问题。

rem的实际应用场景和方法

通过像素密度来控制rem

  1. 通过像素密度(Pixel Density)来控制 rem 布局,需要先了解设备像素比(Device Pixel Ratio,简称 DPR)的概念。
  2. 设备像素比指物理像素和逻辑像素之间的比例关系,通常用于描述高分辨率屏幕上的像素显示细节。

CSS实现

  • 在 CSS 中,可以使用 @media 查询来根据不同的设备像素比设置不同的文档根节点字号大小。

  • 例如,假设文档根节点字号大小为 16px,当设备像素比为 1 时,将其字号大小设置为 16px;

  • 当设备像素比为 2 时,将其字号大小设置为 32px;

  • 当设备像素比为 3 时,将其字号大小设置为 48px,

    以此类推。具体代码如下:

1
2
3
4
5
6
7
@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min--moz-device-pixel-ratio: 1.5),
only screen and (-o-min-device-pixel-ratio: 3/2),
only screen and (min-device-pixel-ratio: 1.5) {
/* 设置文档根节点字号大小为 32px */
html { font-size: 32px; }
}

JavaScript实现

  • 在 JavaScript 中,可以通过获取 window.devicePixelRatio 属性的值来动态设置文档根节点的字号大小。
  • 例如,假设文档根节点字号大小为 16px,当设备像素比为 1 时,将其字号大小设置为 16px;
  • 当设备像素比为 2 时,将其字号大小设置为 32px;当设备像素比为 3 时,将其字号大小设置为 48px
1
2
3
4
5
(function () {
var dpr = window.devicePixelRatio || 1;
var docEl = document.documentElement;
docEl.style.fontSize = 16 * dpr + 'px';
})();

通过以上方式,我们可以根据设备像素比来控制 rem 布局,从而实现适配不同分辨率的设备屏幕,并提高页面的可用性和用户体验。

通过媒体查询屏幕宽度和等分来控制

  • 假设设计稿是750px
  • 我们把整个屏幕划分为10等分(也可以是15、20等分)
  • 那么在320px设备的时候,字体大小为320/20 就是16px
  • 以此类推能实现不同屏幕下,页面元素盒子等比例缩放的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 设置常见的屏幕尺寸, 修改里面的html文字大小
html{
font-size: 50px;
}
// 定义划分是20等份
@no: 20;
// 320
@media screen and (min-width: 320px){
html{
font-size: 320px / @no;
}
}
// 360
@media screen and (min-width: 360px){
html{
font-size: 360px / @no;
}
}
// 375 iphone 678
@media screen and (min-width: 384px){
html{
font-size: 384px / @no;
}
}
@media screen and (min-width: 400px){
html{
font-size: 400px / @no;
}
}
@media screen and (min-width: 414px){
html{
font-size: 414px / @no;
}
}
@media screen and (min-width: 424px){
html{
font-size: 424px / @no;
}
}
@media screen and (min-width: 480px){
html{
font-size: 480px / @no;
}
}
@media screen and (min-width: 540px){
html{
font-size: 540px / @no;
}
}
@media screen and (min-width: 720px){
html{
font-size: 720px / @no;
}
}
@media screen and (min-width: 750px){
html{
font-size: 750px / @no;
}
}

更加简单暴力的方法——通过vw来控制

  • 在 CSS 中,可以将视口宽度作为参照值,然后使用 rem 单位来设置元素的尺寸和间距。
  • 例如,假设希望某个元素的宽度为视口宽度的 50%,可以将其设置为 50vw;
  • 如果希望某个元素的上下间距为视口宽度的 1/10,可以将其设置为 0.1rem。
1
2
3
4
5
6
7
8
html {
font-size: 10vw;
}
.container {
width: 80%;
margin: 1rem auto;
padding: 2rem;
}
  • 在以上代码中,容器元素的宽度被设置为视口宽度的 80%,上下间距被设置为文档根节点字号大小的 1rem(即视口宽度的十分之一),左右间距被设置为 2rem(即文档根节点字号大小的二分之一)。

flexible.js

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1

// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();

// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}

setRemUnit()

// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})

// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))

刨析

  • 这段代码是一个移动端适配方案的实现,主要功能是根据设备的屏幕宽度来设置文本的字号大小和页面中元素的尺寸(以 rem 为单位),从而实现在不同分辨率的设备上显示一致的效果。

该代码的具体实现思路如下:

  1. 获取文档根节点(即 HTML 标签)和设备像素比(devicePixelRatio),并将其赋值给变量 docEl 和 dpr。
  2. 定义函数 setBodyFontSize(),用来设置 body 元素的字号大小。如果 body 元素已经存在,则直接设置其字号大小;否则,等待文档加载完成(DOMContentLoaded)后再设置字号大小。
  3. 调用 setBodyFontSize() 函数,设置 body 元素的字号大小。
  4. 定义函数 setRemUnit(),用来设置文档根节点的字号大小,即将 1rem 设置为视口宽度(docEl.clientWidth)的十分之一。
  5. 调用 setRemUnit() 函数,设置文档根节点的字号大小。
  6. 监听窗口 resize 和 pageshow 事件,当页面发生重绘或重新加载时,重新设置文档根节点的字号大小。
  7. 检测设备是否支持 0.5px 的线条,如果支持,则在文档根节点上添加一个类名 hairlines。

总之,该代码通过设置 body 元素的字号大小和文档根节点的字号大小,以及检测设备是否支持 0.5px 的线条,来实现移动端页面在不同分辨率设备上的适配。