乐趣区

关于css:移动端常见适配方案

做挪动端页面有一段时间了,总结下工作中罕用的几种挪动端适配计划。

根底

网上曾经有十分多的基础知识总结,不再赘诉,详情能够见

《对于挪动端适配,你必须要晓得的》

《不要再问我挪动适配的问题了》

其中容易搞混的概念是 视口

<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,viewport-fit=cover">

meta标签中的 viewport 属性,就是 视图 的含意

视口分为

  • 布局视口
  • 视觉视口
  • 现实视口

布局视口

也就是 <meta name="viewport" content="width=device-width">width属性的含意

咱们在 css 中写的所有款式,就是绝对于 布局视口 进行布局的

默认状况下,挪动端的布局视口并不是屏幕宽度,而是个别在 768px ~ 1024px 间(大部分状况下 980px)

能够通过 document.documentElement.clientWidth 获取 (依据 widthinitial-scale来确定)

视觉视口

视觉视口是指用户通过设施屏幕看到的区域,默认等于以后浏览器的窗口大小 (当initial-scale 为 1)

当用户对浏览器进行缩放时,不会扭转布局视口的大小,所以页面布局是不变的,然而 缩放会扭转觉视口的大小

能够通过 window.innerWidth 获取 (会随着缩放进行扭转)

放大页面,此时 window.innerWidth 反而减小 (页面放大,你看到的货色也变少了)

现实视口

现实视口是指网站在挪动设施中的现实大小,这个大小就是设施的屏幕大小

也就是 <meta name="viewport" content="width=device-width">device-width的含意

能够通过 screen.width 获取 (常量,不会扭转)

initial-scale

<meta name="viewport" content="width=device-width, initial-scale=0.5">

依据公式initial-scale = 现实视口宽度 / 视觉视口宽度

假如现实视口宽度为 414px(device-width),此时设置initial-scale 为 0.5,那么视觉视口宽度就是414 / 0.5 = 818

如果这时你获取 document.documentElement.clientWidth(布局视口) 的值,会发现不是 414px 而是818px

论断: 布局视口宽度取的是 width 和视觉视口宽度的最大值

思考题:

<meta name="viewport" content="width=600, initial-scale=2">

假如现实视口宽度为 414px(device-width),此时document.documentElement.clientWidth(布局视口) 的值是多少?

视觉视口 = 414 / 2 = 207
布局视口 = Math.max(207, 600)
布局视口 = 600

总结

  • document.documentElement.clientWidth: 布局视口,css 中个别写成width=device-width
  • window.innerWidth: 视觉视口,页面缩放都会实时扭转该值
  • screen.width: 现实视口,页面屏幕大小(设施独立像素),也就是 css 中的device-width

常见适配计划

简略一句话概括:挪动端适配就是在进行 屏幕宽度 等比例缩放

平时咱们开发中,拿到的挪动端设计稿个别是 750 * 1334 尺寸大小(iPhone6 的设施像素为规范的设计图)。那如果在750px 设计稿上量出的元素宽度为 100px,那么在375px 宽度的屏幕下,这个元素宽度就应该等比例缩放成50px

所以适配的难点是:如果实现页面的等比例缩放?

Rem 计划

该计划的外围就是:所有须要动静布局的元素,不再应用 px 固定尺寸,而是采纳 rem 绝对尺寸

rem的大小是绝对于根元素 html 的字体大小:如果 htmlfont-size为 100px,那么 1rem 就等于 100px

当初咱们假设:

750px 屏幕下 htmlfont-size 为 100px,也就是 1rem 为 100px,那么 200px 宽度的 .box 元素,就应该写成2rem

.box {
  /* 750px 屏幕下,200px */
  width: 2rem;
}

那么当初:

375px 屏幕下,咱们须要 .box 元素渲染成100px

.box {width: 2rem;}

因为 .box 的宽度依然是2rem,因而,这时候咱们就须要1rem 为 50px,也就是说,此时 htmlfont-size为 50px

于是此时,咱们能够得出一个公式:

(750) / (100) = (以后屏幕尺寸) / (以后屏幕 1rem)

把这个公式进行一次数学转换就能失去:

(以后屏幕 1rem) = 100 * (以后屏幕尺寸) / 750

翻译成 js 语言就是

document.documentElement.style.fontSize = 100 * (document.documentElement.clientWidth) / 750 + 'px';

将代码优化一下

const PAGE_WIDTH = 750; // 设计稿的宽度 
const PAGE_FONT_SIZE = 100;// 设计稿 1rem 的大小

const setView = () => {
  // 设置 html 标签的 fontSize
  document.documentElement.style.fontSize = PAGE_FONT_SIZE * (document.documentElement.clientWidth) / PAGE_WIDTH + 'px';
}
window.onresize = setView; // 如果窗口大小产生扭转,就触发 setView 事件
setView()

思考到 Andorid 端字体渲染的问题以及页面大小变动的监听,最终的代码如下:

(function () {
  var timer = null;
  var PAGE_WIDTH = 750; // 设计稿的宽度 
  var PAGE_FONT_SIZE = 100;// 设计稿 1rem 的大小

  function onResize() {
    var e = PAGE_FONT_SIZE * document.documentElement.clientWidth / PAGE_WIDTH;
    document.documentElement.style.fontSize = e + 'px';
    // 二次计算缩放像素,解决挪动端 webkit 字体缩放 bug
    var realitySize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
    if (e !== realitySize) {
      e = e * e / realitySize;
      document.documentElement.style.fontSize = e + 'px';
    }
  }
  window.addEventListener('resize', function () {if (timer) clearTimeout(timer);
    timer = setTimeout(onResize, 100);
  });
  onResize();})();

留神的是:咱们取 100px 作为设计稿的 1rem,是因为不便计算,比方设计稿上量出250px,咱们就能够很容易的计算出为2.5rem

咱们当然也能够把 50px 作为设计稿的 1rem,这时设计稿上的250px,就要写成5rem

其实咱们也能够借助于 postcss-pxtorem 或者 SCSS 函数来帮咱们主动转换单位

@function px2rem($px) {
  // 根元素字体为 100px
  @return $px / 100 * 1rem;
}

.box {width: px2rem(200);
}

通过 Rem 计划,须要动静缩放的元素,咱们应用 rem 绝对单位,不须要缩放的元素,咱们依然能够应用 px 固定单位。

不过在大屏设施下(例如 ipad 或者 pc 端),因为咱们的页面是等比例缩放,这时候页面的元素会被放大很多(屏幕宽度大,导致根元素字体 1rem 也变大)。然而在大屏下,咱们真正心愿的是用户看到更多的内容,这时候咱们能够应用媒体查问的形式来限度根元素的字体,从而避免在大屏下元素过大的问题。

@media screen and (min-width: 450px) {
  html {font-size: 50px !important;}
}

或者批改 js 脚本的逻辑

const PAGE_WIDTH = 750; // 设计稿的宽度 
let PAGE_FONT_SIZE = 100;// 设计稿 1rem 的大小

const setView = () => {if (document.documentElement.clientWidth > 450) {
    // 大屏下减小根元素字体
    PAGE_FONT_SIZE = 50;
  }
  document.documentElement.style.fontSize = PAGE_FONT_SIZE * (document.documentElement.clientWidth) / PAGE_WIDTH + 'px';
}

VW 计划

vw 是绝对单位,1vw 示意屏幕宽度的 1%

其实咱们的 REM 计划 就是 VW 计划 的模仿,之前咱们有一个公式:

(750) / (100) = (以后屏幕尺寸) / (以后屏幕 1rem)

换一个转换形式:

(以后屏幕 1rem) = (以后屏幕尺寸) / 7.5

而 vw 单位其实就是:

(以后屏幕 1vw) = (以后屏幕尺寸) / 100

因而,REM 计划 就是用 JS 把屏幕宽度分成了 7.5 份,而 CSS3 中新增的 vw 单位,原生实现了把屏幕宽度分成了 100 份

所以,在 VW 计划 中,咱们不再须要应用 JS 脚本了!

750px设计稿中,1vw等于 7.5px(750 / 100),因而,在设计稿中,量出200px 的宽度,就因为写成26.667vw(200 / 7.5)

.box {
  /* 750px 屏幕下,200px */
  width: 26.667vw;
}

不过应用 vw 换算,并不像 rem 那么不便,这时候咱们能够借助 postcss-px-to-viewport 或者 SCSS 函数来帮咱们主动转换单位

@function px2vw($px) {@return $px / 750 * 100vw;}

.box {width: px2vw(200);
}

同样,在大屏设施下,因为屏幕宽度大,所以页面的元素同样会放大很多 (屏幕宽度大,1vw 也很大)。然而因为vw 是绝对屏幕宽度的,所以咱们不能像 REM 计划 中一样,手动管制 html 的根字体大小,这也是应用 VW 计划 的一个毛病。

REM + VW 计划

REM 计划 的劣势是能够手动管制 rem 的大小,避免屏幕太大时,页面元素也缩放很大,然而毛病就是须要应用 JSVW 计划 刚好相同,无需应用 JS 然而无奈手动管制 vw 的大小。

其实咱们能够把两者联合:

html {
  /* 750px 的设计图,1rem = 100px */
  font-size: calc(100 * 100vw / 750);
}

.box {
  /* 750px 屏幕下,200px */
  width: 2rem;
}

对于布局元素,咱们依然应用 rem 单位。然而对于根元素的字体大小,咱们不须要应用 JS 来动静计算了

100 * (document.documentElement.clientWidth) / 750

这段 js 能够间接应用 css 来实现

calc(100 * 100vw / 750)

对于大屏设施,咱们应用媒体查问

@media screen and (min-width: 450px) {
  html {font-size: calc(50 * 100vw / 750);
  }
}

更具体的vw+rem 布局计划 能够见《基于 vw 等 viewport 视区单位配合 rem 响应式排版和布局》

viewport 缩放计划

还有一种更简略粗犷的办法,就是咱们设置initial-scale

咱们的布局齐全基于设计稿 750px,布局元素单位也应用px 固定单位 (布局视口写死 750px)

对于 375px 宽度,咱们就将整个页面缩放0.5

<meta name="viewport" content="width=750, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5, user-scalable=0">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>demo</title>
  <script>
    var clientWidth = document.documentElement.clientWidth;
    var viewport = document.querySelector('meta[name="viewport"]');
    var viewportWidth = 750;
    var viewportScale = clientWidth / viewportWidth;
    viewport.setAttribute('content', 'width=' + viewportWidth + ', initial-scale=' + viewportScale + ', minimum-scale=' + viewportScale  + ', maximum-scale=' + viewportScale + ', user-scalable=0');
  </script>
</head>
.box {width: 200px;}

此计划的毛病: 整个页面都被缩放了,对于不想缩放的元素无法控制。

市面上一些营销 H5 页面,因为是通过后盾可视化拖拽搭建进去的,为了适配各种尺寸的屏幕,该计划是老本最低的实现(易企秀就是应用这种计划)

实战

咱们拿线上 B 站的会员购作为示例

  • rem 计划
  • vw 计划
  • rem+vw 计划
  • viewport 计划

请应用 chrome 开发者工具模仿挪动端设施查看

源码间接右键查看即可,代码没有通过压缩,能够很直观的看到各种计划的 css 适配写法

参考

  • 挪动端适配有哪几种计划?
  • 不要再问我挪动适配的问题了
  • 对于挪动端适配,你必须要晓得的
  • 基于 vw 等 viewport 视区单位配合 rem 响应式排版和布局
退出移动版