web移动端UI适配之rem

移动端适配大致两种,一种是百分比还有一种就是rem,这里就说说rem适配要怎么做,原理是什么。先说说为什么要做适配吧。

为何要适配?

众所周知,手机屏幕的尺寸是五花八门的,像素密度也是不一样的。同样是宽度5cm的屏幕,有的手机是720px,有的手机是1080px。

那么假设设计师有个设计稿,宽度是720px的,设计稿里有张图,这张图的宽度也是720px。如果我们直接在代码里写这张图的宽度为720px,那我们在720px的手机上看则是正常的,这里我们用一个div做演示

720px屏幕

但是当用户的手机屏幕宽度有1080px的时候呢?看起来就会像这样
1080px屏幕

很显然这跟预期的不一样,所以我们需要div的尺寸跟随实际屏幕的宽度进行缩放。这时,我们的rem就刚好满足需求。

rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。

简单来说,我们设定html元素的font-size等于100px时,1rem就等于100px。1 rem = 1 font-size

所以我们只需要获取屏幕的宽度,然后根据宽度和设计稿的比例来计算html的font-size,并设置上去。
在写代码的时候,就用rem代替px,这样就可以使div的尺寸根据html的font-size缩放了

如何计算font-size?

我们来做几个设定

  • 设计稿宽度为750px
  • 设计稿中有一个div,宽度也是750px
  • 理想状态(实际屏幕宽度也是750px)下html的font-size是100px,即1rem=100px

那么,我们先手动写一下理想状态

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>rem适配</title>
    
</head>
<style>
    body {
        margin: 0;
        font-size: 16px;;
    }
    html {
        font-size: 100px;
    }
</style>
<body>
    <div style="width: 7.5rem;background: yellow;">
        750px/100 = 7.5rem div
    </div>
</body>
</html>

一定要写body下的font-size=16px,或者你需要的,不然body元素下的文字默认大小就会继承html下的100px,那样的话,字体就太大了。

好了,来看效果
750px下的7.5rem

一切正常,我们来把屏幕像素稍微调大一点,把屏幕宽度调成900。
900px下的7.5rem

由于我们没有动态调节html的font-size,所以div的宽度没有跟随变化。那么就来计算一下。

先算出100px是设计稿的多少倍,然后再乘以实际屏幕宽度,就能算出缩放后的font-size了。
100/750*900 = 120px
现在把120设置上去

html {
     font-size: 120px;
}

搞定~

900px下的7.5rem

好了,rem适配的基本原理你已经知道了,我们开始编码。
新建一个flexible.js

// designWidth:设计稿的实际宽度值,需要根据实际设置
// maxWidth:支持缩放的最大屏幕宽度值,当屏幕宽度超过此值则按最大值进行计算,需要根据实际设置
// 这段js的最后面有两个参数记得要设置,一个为设计稿实际宽度,一个为支持缩放的最大屏幕宽度值,例如设计稿为750,最大宽度为750,则为(750,750)
(function(designWidth, maxWidth) {
    var doc = document
    var win = window
    var docEl = doc.documentElement
    var remStyle = document.createElement('style') // 创建一个style标签,用于写html样式
    var tid //timeout句柄
  
    // 计算rem的值,即1rem等于多少px
    function refreshRem() {
      var width = docEl.getBoundingClientRect().width // 获取屏幕宽度
      maxWidth = maxWidth || 750 // 当没写maxWidth时,默认maxWidth=750
      // 当屏幕宽度超过最大缩放的值,则按最大值处理,不能无限缩放,那样会变得很大,很丑
      if (width > maxWidth) {
        width = maxWidth
      }
      // 计算html的font-size
      var rem = 100 / designWidth * width
      remStyle.innerHTML = 'html{font-size:' + rem + 'px;}'
    }
  
    // 将style标签插入到文档
    if (docEl.firstElementChild) {
      docEl.firstElementChild.appendChild(remStyle)
    } else {
      var wrap = doc.createElement('div')
      wrap.appendChild(remStyle)
      doc.write(wrap.innerHTML)
      wrap = null
    }
    // 要等 wiewport 设置好后才能执行 refreshRem,不然 refreshRem 会执行2次;
    refreshRem()
  
    win.addEventListener('resize', function() {
      clearTimeout(tid) // 防止执行两次
      tid = setTimeout(refreshRem, 300)
    }, false)
  
    win.addEventListener('pageshow', function(e) {
      if (e.persisted) { // 浏览器后退的时候重新计算
        clearTimeout(tid)
        tid = setTimeout(refreshRem, 300)
      }
    }, false)
  
    // 设定body的默认字体大小,如果不设置,将集成html的字体大小,而我们设置的100,so...
    if (doc.readyState === 'complete') {
      doc.body.style.fontSize = '16px'
    } else {
      doc.addEventListener('DOMContentLoaded', function(e) {
        doc.body.style.fontSize = '16px'
      }, false)
    }
  })(750, 750)
// 这里我们输入设计稿750px,支持的最大屏幕宽度也是750px。

可能有同学会问,为什么只有750px,现在手机都是1080px了。

其实浏览器返回给js和css的px都是抽象的,是根据真实像素和ppi计算出来的,像我的小米8,1080px宽度,js获取的屏幕宽度也只有393px

并且一定要在head元素里写 <meta name="viewport" content="width=device-width, initial-scale=1.0">
关于viewport是啥,可以参阅 https://blog.csdn.net/lamanchas/article/details/78473249

最后,我们只需要在html中引用此文件即可,也可以用es6的方式引用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>rem适配</title>
    <script src="./flexible.js" type="text/javascript"></script>
    <style>
    body {
        margin: 0;
    }
    </style>
</head>
<body>
    <div style="width: 7.5rem;background: yellow;">
        750px/100 = 7.5rem div
    </div>
</body>
<script>
</script>
</html>

最后效果

25.gif

添加新评论