移动端适配方案

rem方案

rem是相对于html节点的font-size来做计算的一个相对单位,在运行时动态修改html节点的font-size大小,从而影响所有使用rem单位的布局。

1rem该设为多少?其实没有什么统一的标准。国内主流网站也都在使用rem方案,具体方案和阿里的大同小异,根据是否缩放viewport分为两个阵营:

不缩放viewport(屏幕宽度为375px):

  • 马蜂窝 1rem = 37.5px
  • 小米 1rem = 52.0833px
  • 小红书 1rem = 50px
    缩放viewport(屏幕宽度为375px,viewport scale为0.5):
  • B站 1rem = 46.875px
  • 搜狐 1rem = 75px
    最早的rem方案是阿里早期提出并在手机淘宝上使用,同期封装开源了lib-flexible适配库,下面以flexible的方案为例介绍:
    首先约定将设备的布局视窗进行10等分,将html节点的font-size设置为页面布局视窗1/10,即1rem就等于页面布局视窗1/10,这就意味着我们后面使用的rem都是按照页面比例来计算的。
    iPhone6为例:布局视窗为375px,则1rem = 37.5px,这时UI给定一个元素的宽为75px,我们只需要将它设置为75 / 37.5 = 2rem
    接下来要做的就是,根据设备的独立像素比,在运行时控制根节点font-size的大小,使用css或者js都可以实现:
    一种是媒体查询,优点:不需要额外使用js去更改html的字体,缺点:不连续,或者说并能完全实现对所有设备的布局规范统一;
html {
   font-size: 50px
 }
 @media only screen and (min-device-width: 375px) and (-webkit-min-device-pixel-ratio: 2) {
   html {
     font-size: 37.5px
   }
 }
 @media only screen and (min-device-width: 360px) and (-webkit-min-device-pixel-ratio: 3) {
   html {
     font-size: 36px
   }
 } 

另一种是js动态更改html字体,优点:连续;缺点:不如直接写媒体查询的体验好;

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

该方案是阿里手淘早期使用,并封装开源了lib-flexible库,它的主要思想有三点:

  • 根据dpr的值来修改viewport实现1px的线
  • 根据dpr的值来修改htmlfont-size,从而使用rem实现等比缩放
  • 使用Hack手段用rem模拟vw特性
    存在的问题:
  • 需要额外内联引入一个lib-flexible库,保证在所有资源加载之前执行这个JS,使用iframe引用就会出现问题。
  • htmlfont-size设置到12px以下还是会按照12px=1rem来计算,这样所有使用了rem单位的尺寸都是错的。
  • 在奇葩的dpr设备上表现效果不太好,比如一些华为的高端机型 用rem布局会出现错乱。

vw方案

1. 原理

vw是相对单位,1vw表示屏幕宽度的1%。基于此,我们可以把所有需要适配屏幕大小等比缩放的元素都使用vw做为单位。不需要缩放的元素使用px做单位。

举个例子。设计师交付的设计稿宽度是750px,设计稿上一个标题的fontSize标注尺寸是32px。(32/750)*100% = 4.27% ,换句话说这个标题的字号占屏幕宽度的占比是4.27%,不管任何屏幕都是如此。4.27% 即 4.27vw。

对于任何需要等比缩放的元素,在写CSS设置样式时直接换算成vw即可,尺寸 = 100vw*设计稿标注大小/设计稿宽度。

2. 适配代码

假设设计稿尺寸是750px,页面有一个按钮,按钮文字标注大小28px,按钮高度标注为48px,宽度为120px,边框为1px不缩放。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    .button {
      width: 16vw;/*  100vw*120/750  */
      font-size: 3.73vw;/*  100vw*28/750  */
      line-height: 6.4vw;/*  100vw*48/750  */
      border: 1px solid #000;/*不需要缩放的部分用px*/
      text-align: center;
    }
  </style>
</head>
<body>
  <div class="button">按钮</div>
</body>
</html>

在写样式时会发现,虽然不用写JS做适配,但标注尺寸换算为vw又麻烦又不直观。

我们可以在CSS里使用calc来换算换,只不过需要注意新语法的兼容性。

:root {
  --ratio: calc(100vw/750);
}
.button {
  font-size: calc(100vw*28/750);/* 可以直接用calc */
  line-height: calc(100vw*48/750);

  width: calc(120*var(--ratio));/* 也可以用calc配合var使用,IE不支持var */
  border: 1px solid #000;/*不需要缩放的部分用px*/
  text-align: center;
}

在正式的项目里,我们也可以用SCSS,把换算交给预处理器

@function px2vw($px) {
  @return $px * 100vw / 750;
}
.button {
  width: px2vw(120);
  font-size: px2vw(28);
  line-height: px2vw(48);
  border: 1px solid #000;
  text-align: center;
}

3. 借助 postcss-px-to-viewport,

将 px 自动转为 vw

1. 安装 postcss-px-to-viewport:yarm add postcss-px-to-viewport -D

2. 在项目根目录下添加postcss.config.js文件

module.exports = {
  plugins: [
    // to edit target browsers: use "browserslist" field in package.json
    require('autoprefixer'),
    require("postcss-px-to-viewport")({
      unitToConvert: "px", // 要转化的单位
      viewportWidth: 375, // UI设计稿的宽度
      unitPrecision: 6, // 转换后的精度,即小数点位数
      propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
      viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
      fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
      selectorBlackList: ["wrap"], // 指定不转换为视窗单位的类名,
      minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
      mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
      replace: true, // 是否转换后直接更换属性值
      exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
      landscape: false // 是否处理横屏情况
    })
  ]
}

3. 然后重新运行项目,使配置文件生效

适配方案对比

  • 动态REM方案
    • 适配原理稍复杂
    • 需要使用JS
    • 设计稿标注的px换算到CSS的rem计算简单
    • 方案灵活技能实现整体缩放又能实现局部不缩放
  • vw方案
    • 适配原理简单
    • 不需要JS即可适配
    • 设计稿标注的px换算到CSS的vw计算复杂
    • 方案灵活技能实现整体缩放又能实现局部不缩放
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值