采用rem进行PC端和移动端的适配(三种方式)

1,743 阅读6分钟

作为一个前端工程师,适配不同显示器的分辨率算是基本功了,大到各种显示器:2k屏,1920,1280,小到各种手机尺寸,不适配的话界面必定混乱,在前端来说算是bug了,目前主流的适配方式为rem和vw,vh,微信小程序的话直接rpx更方便

为何选用rem而不选用vw,vh

vw,vh的利弊

vw,vh是指把屏幕分成100等份,横屏不论多大,都为100vw,竖屏不论多长都为100vh,操作时可以换算,例如一个375px的设计稿,1vw即为375/100=3.75px,假如一个width为100px的盒子,则长度为100/375 × 100 =26.66 vw

这样就该盒子不论在哪个屏幕,长度都为100份中的26.66份,相当于375中的100,这样尺寸就对上了

rem的利弊

rem是指根字体的大小,即html的font-size,假如html的根字体大小为16px,则rem=16px,所以实现rem的关键就是根据屏幕尺寸设置根字体大小,再根据"根字体"大小转化实际尺寸

实现原理其实跟vw差不多,先把屏幕分为n份rem(n可以随意,主流为10,15,20),这里以10份为例,设置根字体rem的大小为 屏幕尺寸/10

  • 假如分辨率为375,则10rem=375px,即1rem为375/10=37.5px,一份375的设计稿100px,就应该为100/(375/10)= 2.66rem
  • 假如分辨率为414,则10rem=414px,即1rem为414/10=41.4px,2.66rem为41.4*2.66=110.1px
  • 假如分辨率为750,则10rem=750px,即1rem为750/10=75px,2.66rem为75*2.66=199.5≈200px

即不论在什么界面,长度都为2.66rem,所以关键就是设置rem的值,即根字体大小

选择rem的原因

vw非常简单并且好用,这是优点,但只限于移动端

vw在PC端有个很大的缺点,必须每个设置最小值,否则当窗口拉取到很小的时候,就得变形,甚至有时移动端也需要大量的设置最小值,同时也会有最大值的需求,如果有大佬有能一次性设置最值的方式,欢迎交流沟通

rem前期设置更麻烦,但配置好了,不会有最大最小界面的问题

只需要设置一个最大的rem和最小的rem,即可保证界面的最大状态是最小状态

rem适配的3种方式

一、媒体查询,根据屏幕尺寸,设置rem的值

假设目前需要适配移动端320,360,375,1920的分辨率,才用scss,设置一个变量$no为10,用于分10份,

  • @media---开启媒体查询
  • screen---查询的类型(屏幕宽度)
  • max-width最大值
  • min-width最小值

global.scss,在main.js中引入

$no: 10;
//表示宽度最大值为320的情况下,设置rem为 320/10 px,即32px
//所以只要小于320px,统一rem为32px,这样就设置了最小值
@media screen and (max-width: 320px) {
  html {
    font-size: 320px / $no
  }
}

//表示宽度最小值为320的情况下,设置rem为 320/10 px,即32px
//所以只要大于320,rem值就为32px
@media screen and (min-width: 320px) {
  html {
    font-size: 320px / $no
  }
}

//表示宽度最小值为360的情况下,设置rem为 360/10 px,即36px
//所以只要大于等于360,rem的值就为36px
@media screen and (min-width: 320px) {
  html {
    font-size: 320px / $no
  }
}

//下面也是同理,同时利用css下方样式的覆盖性,只要按顺序写就能保证能检测到
//表示宽度最小值为375的情况下,设置rem为 375/10 px,即37.5px
//所以只要大于等于375,rem的值就为37.5px
@media screen and (min-width: 375px) {
  html {
    font-size: 375px / $no
  }
}

//PC端适配--使用pc端的分辨率就行
@media screen and (min-width: 1920px) {
  html {
    font-size: 1920px / $no 
  }
}

//最后设置个最大值,max-width: 1920px--->最大值为1920px
//只要小于1920px,统一rem为192px,这样就设置了最大值
@media screen and (max-width: 1920px) {
  html {
    font-size: 1920px / $no 
  }
}

最后在网页中使用rem做单位,假如100px在375px中长度就用2.66rem做单位(有插件可以转换,下文介绍)

  .box {
    width: 2.66rem;
    height: 2.66rem;
  }

二、媒体查询+flexible.js

flexible.js就是手淘团队推出转化rem的方法,复制引用就好,原理就是给每个html标签添加个行内样式 style="font-size: xxx px "

(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();
   
   
  //这里也是设置的份数,也是用10
  // set 1rem = viewWidth / 10 
  // 把屏幕平均分成10等份。比如1920/10= 192 px,这个时候1rem就是192px,配合vscod插件快速适配,在style中使用媒体查询。
  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))

在main.js引入 import '@/utils/flexible'

设置最大值和最小值,加个important,提升权重

@media screen and (max-width: 320px) {
  html {
    font-size: 320px / $no!important
  }
}

@media screen and (max-width: 1920px) {
  html {
    font-size: 1920px / $no !important
  }
}

插件转化的问题

vscode安装cssrem插件

Snipaste_2024-12-22_22-36-07.png 找到设置根字体大小的地方,假设设计稿为1920,设置为10等份,那根字体大小就为1920/10=192,之后写css的时候,只要写 xx px,即会有转化rem选项

Snipaste_2024-12-22_22-36-42.png

三、postcss-pxtorem插件自动转化

安装

可能会有版本问题,遇到可以安装postcss-pxtorem@5.1.1

npm install postcss postcss-pxtorem --save-dev
配置

在你的项目根目录中创建一个 postcss.config.js 文件,并添加以下配置:

//配置自行选择使用,rootValue和propList应该为必选项
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 37.5, // 表示根元素字体大小或根据input参数返回根元素字体大小
      propList: ['*'], // 可以从px更改为rem的属性, 通配符*表示启用所有属性
      selectorBlackList: ['.norem'], // 过滤掉.norem开头的class,不进行rem转换
      replace: true, // 是否直接替换原来的 px 单位
      mediaQuery: false, // 是否转换媒体查询中的 px
      minPixelValue: 1// 设置要替换的最小像素值
    }
  }
}
创建utils/rem.js,在main.js中引入,之后直接使用单位px,会自动转化
// 配置基本大小
let baseSize = 37.5;

// 设置 rem 函数
function setRem () {
  //当前页面宽度相对于375px屏幕宽的缩放比例,可根据自己需要修改。
  let scale = document.documentElement.clientWidth / 375;
  //设置页面根节点字体大小(“Math.min(scale, 2)” 指最高放大比例为2,即最大根字体大小为37.5*2=75,可根据实际业务需求调整)
  let fz = baseSize * Math.min(scale, 2)
  //获得根字体大小后,给全部页面设置根字体大小
  //业务需要限制显示页面的最小值,我这里是根据倍数限制的,因为1倍的字体大小为37.5,具体情况可以自己设定
  document.documentElement.style.fontSize = scale >= 1 ? fz + 'px' : 37.5 + 'px'

}
setRem(); //初始化

// 适配 - 重置函数
function resetRem (num) {
  if (num) baseSize = Number(num);
  setRem();
}
window.resetRem = resetRem; // 全局可调用(其他方式也可)

// 改变窗口大小时重置 rem
window.onresize = function () {
  setRem()
};

三种方式都可以实现适配,个人感受如下:

  • 第一种比较原生,每多适配一个就得多写一个媒体查询
  • 第二种属于第一种的进阶版
  • 第三种为插件转化,更方便,但可能会有版本兼容的问题

(封面来源于网络,如有侵权,请联系删除)