前端笔记 —— px 转 rem

2,462 阅读4分钟

在项目的开发中,我们需要对屏幕进行适配,适配的方式有很多种,今天我们就来了解其中一种 —— rem,相信大家对这个东西一点都不陌生,那么它和 px 之间是哪种关系呢?让我们一起来学习吧~

rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到 rem 大家一定会想起 em 单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。 所以这里总结一句,所谓依赖根元素来计算的方式,就是先给予 html 元素一个font-size,然后我们所有的 rem 就根据这个 font-size 来计算的。

这里举个例子:

htmlfont-size25px(也就是 1rem = 25px),那么要设置 50px 转换为 rem,就是 50 / 25 = 2rem,假设现在 1rem = 750px 那么 100px 就等于 100px / 750px。现在我们设想在不同手机的不同屏幕的情况下,得到的结果就不一样,这时,就需要通过动态获取屏幕的宽度得到最后的 font-size,假设我们现在的视口是 iphone 6s,那么 1rem = 375 / 750,因此,1rem 就为 0.5px,由于小数不好换算,所以我们将进行优化 1rem = 375 / 750 * 100,这时 1rem = 50px

因此,我们用代码来表示这段文字:

var windowWidth = document.documentElement.getBoundingClientRect().width || window.innerWidth;
var RootFontSize = windowWidth / 750 * 100;
document.getElementsByTagName("html")[0].style.fontSize = RootFontSize + "px";

现在,让我们在 vscode 中安装 cssrem 这个插件,并设置 cssrem.rootFontSize50,然后重启 vscode,接下来在 css 中写 px 时,它会自动为我们转换为 rem,如图:

如果想要多多了解 cssrem 这个插件的同学,可以自行学习,这个插件很简单,在这里就不一一赘述了~ღ( ´・ᴗ・` )

现在有一个问题,当我们设置 border1px 时,在各个手机上的表现形式各不相同,在有的手机上看起来比较粗,而在有的手机上看起来比较细,这是为什么呢?

回想一下,设备的设备像素比是由物理像素与 css 像素的缩放比例而来,那么当设备像素比为 1 时,css 与物理像素的对应关系是一对一的(一个 css 像素表示一个物理像素),当设备像素比为 2 时,css 与物理像素的对应关系是一对二的(一个 css 像素表示两个物理像素)。以此类推,当设备像素比发生变化时,我们的 css 对应的物理像素会随之改变,因此,我们可以通过动态设置缩放比例来进行适配:缩放比 = 1 / 设备像素比

⚠️ 缩放比例的设置是基于 px 转换为 rem

因此,我们来动态设置视口的缩放比例:

// 获取 dpr
var docEl = document.documentElement,
    viewportEl = document.querySelector('meta[name="viewport"]'),
    dpr = window.devicePixelRatio || 1;// 获取像素比
// 设置 dpr,这里固定为 1,2,3
dpr = dpr >= 3 ? 3 : (dpr >= 2 ? 2 : 1)
// 设置缩放比例
var scale = 1 / dpr
// 缩放比例生成 content
var content = 'width=device-width,initial-scale='+ scale +',user-scalable=no'
// 若有 meta 为 viewport 属性,则设置这条 meta 的 content,若没有,则重新生成一个并且设置 content
if (viewportEl) {
    viewportEl.setAttribute('content', content)
} else {
    viewportEl = document.createElement('meta')
    viewportEl.setAttribute('name', 'viewport')
    viewportEl.setAttribute('content', content)
    document.head.appendChild(viewportEl)
}

⚠️ 当我们设置了缩放比后,px 与物理像素的对应关系永远为 1,那么在大屏下看起来就会相对较小,但是对于边框来说,越精细效果越好,若不需要适配 1px 的边框,则不需要对缩放比进行动态设置。

最后,完整的代码如下:

(function() {
  "use strict"
  // 获取 dpr
  var docEl = document.documentElement,
      viewportEl = document.querySelector('meta[name="viewport"]'),
      dpr = window.devicePixelRatio || 1; // 获取像素比
  // 设置 dpr,只能为 1,2,3
  dpr = dpr >= 3 ? 3 : (dpr >= 2 ? 2 : 1)
  // 设置缩放比例
  var scale = 1 / dpr
  // 缩放比例生成 content
  var content = 'width=device-width,initial-scale='+ scale +',user-scalable=no'
  // 若有 meta 为 viewport 属性,则设置这条 meta 的 content,若没有,则重新生成一个并且设置 content
  if (viewportEl) {
      viewportEl.setAttribute('content', content)
  } else {
      viewportEl = document.createElement('meta')
      viewportEl.setAttribute('name', 'viewport')
      viewportEl.setAttribute('content', content)
      document.head.appendChild(viewportEl)
  }
  var maxWidth = 1024, // 设置最大屏
      minwidth = 320, // 设置最小屏
      windowWidth = docEl.getBoundingClientRect().width || window.innerWidth;
  // 若超过最大宽度,就缩放到最大宽度为止,这里的 dpr 表示缩放比例,由于自动设置了缩放比例,改变了原有的像素比,所以需要除去 dpr 后再进行比较
  if (maxWidth && (windowWidth / dpr >= maxWidth)) {
      windowWidth = maxWidth * dpr
  } else if (minwidth && (windowWidth / dpr <= minwidth)) {
      // 若小于最小宽度,就缩放到最小宽度为止
      windowWidth = minwidth * dpr
  }
  var RootFontSize = windowWidth / 750 * 100;
  document.getElementsByTagName("html")[0].style.fontSize = RootFontSize + "px";
})()

最后,本文只是个人见解,如有不正确的地方,请大家大方指出,我们一起相互学习相互进步嘻嘻