rem移动端适配方案

1,505 阅读3分钟

rem单位是跟随网页根元素的fongt-size改变自动改变的,是一个相对的长度单位,非常适合在不同手机界面上自适应屏幕大小。

一般手机浏览器的默认字体大小是16px,即

:root {
    font-size: 16px;
}
/* 在html网页中,:root选择器相当于html*/

也就是说,我定义一个宽度为1rem,实际宽度就是16px;

反过来说,一个ihone6设备获取的设备宽度是375px,375 ➗ 16 = 23.4375 即如下才能铺满全屏

body {
    width: 23.4375rem;
    height: 100%;
    background-color: #000
}

一个iphone6 plus的宽度是414,那

body {
    width: 25.875rem;
    height: 100%;
    background-color: #000
}

虽然上述实现了设计要求的全屏,但是会发现真实的问题; 一个是很明显,rem换算起来极其不方便;第二个浏览器根字体大小不确定

所以我们想如果rem和px换成简单,比方就是10,那么 一个iphone6 plus的宽度是414,那

:root {
    font-size: 37.5px;
}
body {
    width: 10rem;
    height: 100%;
    background-color: #000
}

一个iphone6 plus的宽度是414,那

:root {
    font-size: 41.4px;
}
body {
    width: 10rem;
    height: 100%;
    background-color: #000
}

这样只要根据设备宽动态设定好根元素的font-size,那么就可以很自由的使用rem了

所以要适配不同屏幕的手机,就只需要按照比例换算更改:root的font-size即可;这个比例就由窗口宽度 ÷ 设计宽度得到(简称窗设比

比方设计稿750px,窗口宽度375px(为何是两倍设计稿,查看原因; 那么:

窗设比 = 375 / 750 = 0.5
根元素的fontSize = 16 * 0.5 = 8px

1.2 但是

原本这样就已经大功告成,但是在实际中,部分安卓手机(如华为某旗舰机)不支持:root的font-size小于12px; 其他一些浏览器也会出现小于12px的文字已12px显示。

为了避免在任何环节出现根元素小于12px的font-size值,且不增加设计稿尺寸到css数值换算的难度,一般都采用放大缩小法

    1. 约定**样设比(设计稿的px值与css的rem值比)**为100; 也就是设计稿750px,写css时写7.5rem
    1. 使用js将:root的font-size重置为“样设比”与“窗设比”的乘积
    1. 计算CSS时的rem值只需要从设计稿获取px值再除以“样设比”100即可
窗设比 = windowWidth / designWidth =  (比方)375 / 750 = 0.5
样设比 = 100
rootFontSize = 样设比 * 窗设比
cssOneWidth = 50px 
cssRem = cssOneWidth / 样设比 = 7.5rem

则css实际px = cssRem * rootFontSize = (cssOneWidth / 样设比 ) * 样设比 * 窗设比 = cssOneWidth * 窗设比 = 50 * 0.5 = 25px

完整的代码vue版本(我们项目主要做横屏,以iphone6为标准)

const maxWidth = 1624;

export function setFontSize(designWidth = 1334, designHeight = 750) {
  const minAspectRatio =  designHeight / maxWidth;
  // 网页可见区域宽
  let clientWidth = document.documentElement.clientWidth || document.body.clientWidth || window.innerWidth || window.screen.width
  // 网页可见区域高
  const clientHeight = document.documentElement.clientHeight || document.body.clientHeight || window.innerHeight || window.screen.height
  // 设备纵横比
  const deviceAspectRatio= clientHeight / clientWidth
  // 设计纵横比
  let designAspectRatio =  designHeight / designWidth;
  // 横屏
  if (deviceAspectRatio < 1) {
    if (deviceAspectRatio < minAspectRatio) {
      // 宽比maxWidth还宽
      designWidth =  maxWidth
    } else if (deviceAspectRatio < designAspectRatio) {
      clientWidth = clientHeight / designAspectRatio
    }
  }
  
  window.clientWidth = clientWidth

  console.log({
    devicePixelRatio: window.devicePixelRatio,
    dclientWidth: document.documentElement.clientWidth,
    bClientWidth: document.body.clientWidth,
    innerWidth: window.innerWidth,
    screenWidth: window.screen.width,
    deviceAspectRatio,
    minAspectRatio,
    clientWidth,
  })

  const fontSize = 100 * ( clientWidth / designWidth) ;
  window.__REM_FONTSIZE__ = fontSize;

  document.documentElement.style.fontSize = `${fontSize}px`;
}

至于像素出现小数问题,即rem经过换算成px时,出现诸如25.5px的问题;

浏览器在处理小数像素的时候并不是直接舍入处理的,元素依旧占据着应有的空间,只是在计算元素尺寸的时候做了舍入处理

目前遇到最多的问题就是 background-image 的问题,经常会因为小数像素导致背景图被裁掉一部分。

解决

  • 使用iconfont
  • 如果使用 background-image,尽量为背景图设置一定的空白间隙,就是给图片的父盒子要比图片大一点,图片居中。比方图片大小最终为67.1875px x 67.1875px 就给盒子 72px X 72 px