如何进行移动端的样式适配

444 阅读6分钟

在前端面试中我们可能经常会被问到:

  • 你常用的宽高等样式单位有哪些?
  • 如何实现响应式布局?
  • 如何进行移动端的样式适配?
  • 1px 问题
  • ...

响应式布局和自适应布局的区别:

  • 自适应布局:多套代码,根据UA检测,重定向到不同的网页,需要刷新页面,可查看京东官网
  • 响应式布局:一套代码,根据屏幕自动改变页面样式,不需要刷新页面,可查看掘金官网

一、CSS 样式单位

px

px 表示像素

  • MDN 中,px 被认为是绝对长度单位,因为像素就是显示器上的一个个小点,每个小点的大小是相同的。
  • 有些人则认为 px 属于相对长度单位,因为在移动端存在设备像素比,所以 px 的实际大小是不确定的。

px 的特点:

  • 在 PC 端经常使用;
  • 直接使用,方便快捷;
  • 不能根据屏幕进行自适应;

rpx

rpx(responsive pixel) 是微信小程序中的css尺寸单位,属于相对长度单位

rpx 的特点:

  • 微信小程序使用;

  • 可根据屏幕自适应;

    微信规定:屏幕宽为 750px,如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

    设备rpx 换 px(屏幕宽度 / 750)px 换 rpx(750 / 屏幕宽度)
    iphone 51rpx = 0.42px1px = 2.34rpx
    iphone 61rpx = 0.5px1px = 2rpx
    iphone 6 plus1rpx = 0.552px1px = 1.81rpx

%

%,相对长度单位,相对于父元素的比例

<div class="box">
    <div class="box1">box1</div>
</div>
<style>
    .box {
      width: 100px;
      font-size: 30px;
      height: 110px;
      border: solid 1px green;
    }
    .box1 {
      width: 40%;
      height: 100px;
      border: solid 1px red;
      font-size: 50%;
    }
</style>

image.png

em

em 是相对长度单位。在 font-size 中使用是相对于父元素的字体大小(如果没有设置父元素的字体大小,则相对于浏览器的默认字体大小(16px)),在其他属性中使用是相对于自身的字体大小。

其实本质上 em 还是相对于自身的 font-size1em = 自身的 font-size),因为 font-size 具有继承性。

em 的使用

  • 子元素设置了 font-size
.box1 {
    font-size: 20px;
    width: 1em;
}

image.png

  • 子元素未设置 font-size,父元素设置了 font-size
.box {
    font-size: 30px;
}
.box1 {
    width: 1em;
}

image.png

  • 子元素未设置 font-size,父元素也未设置 font-size

image.png

rem

rem(font size of the root element),相对长度单位,相对于根元素(<html>font-size

1rem = html 根元素设定的 font-size 的 px 值(如果没有设置就是 1rem = 16px)

vw / vh

网页视口尺寸:
window.screen.height // 屏幕高度
window.innerHeight // 网页视口高度
document.body.clientHeight // body 高度

vw(viewport's width)和 vh(viewport's height)是相对长度单位,相对于网页视口的宽高

  • vh 网页视口高度的 1%;
  • vw 网页视口宽度的 1%;
  • vmax 取vh 和 vw 的最大值;
  • vmin 取vh 和 vw 的最小值。

⚠️ 注意区分 vw/vh 和 %。

二、 响应式布局的常见方案

media-query + rem

使用媒体查询(media-query),根据不同的屏幕宽度设置根元素 font-size

html { font-size: 20px; }
@media only screen and (min-width:320px) { html { font-size: 17.067px } }
@media only screen and (min-width:360px) { html { font-size: 19.2px } }
@media only screen and (min-width:375px) { html { font-size: 20px } }
@media only screen and (min-width:414px) { html { font-size: 22.08px } }
@media only screen and (min-width:480px) { html { font-size: 25.6px } }
@media only screen and (min-width:640px) { html { font-size: 34.13px } }
@media only screen and (min-width:720px) { html { font-size: 38.918px } }
@media only screen and (min-width:750px) { html { font-size: 40px } }

@media (min-width:800px) { html { font-size: 20px } }

/* ----------- Non-Retina Screens ----------- */
@media screen 
and (min-device-width:1200px) 
and (max-device-width:1600px) 
and (min-device-pixel-ratio:1) {
  html {
    font-size: 20px;
  }
}

/* ----------- Retina Screens ----------- */
@media screen 
and (min-device-width:1200px) 
and (max-device-width:1600px) 
and (min-device-pixel-ratio:2)
and (min-resolution:192dpi) {
  html {
    font-size: 20px;
  }
}

缺点:

  • 成本大,不同尺寸需要写多个 @media

viewport 适配方案

css 单位使用 vw/vh,vw/vh 作为布局单位,从底层根本解决了不同尺寸屏幕的适配问题,因为每个屏幕的百分比是固定的、可预测、可控制的。

如果我们拿到的视觉稿宽度为750px,视觉稿中某个字体的大小为75px,我们的 css 只需这样写:

.box {
    font-size: 10vw;  // 1vw = 750px * 1% = 7.5px
}

设置 meta 标签

在 html 头部设置 meta 标签如下,让当前 viewport 的宽度等于设备宽度,同时不允许用户手动缩放:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />

px 自动转 vw

使用 postcss-px-to-viewport 插件完成 px 单位向视口单位的转换。

三、1px 问题

1px 问题出现的原因

设计稿上的 1px 是指设备像素 1px,但是我们设置 CSS 大小为 1px 的时候,在 dpr(设备像素比)为 2 时,则等于 2px 设备像素,即 1px CSS 像素需要 2 个物理像素进行渲染;在 dpr(设备像素比)为 3 时,则等于 3px 设备像素,即 1px CSS 像素需要 3 个物理像素进行渲染,所以看到的会比实际的粗一些。

dpr(设备像素比),是设备上物理像素和设备独立像素的比例,即物理像素 / dip。可以使用 window.devicePixelRatio 查看设备像素比。
dip / dp(设备独立像素)与屏幕密度有关。

解决方案:transform + :before/:after + 媒体查询

// util.scss
@mixin _hairline($x, $y, $type) {
  position: absolute;
  content: '';
  #{$x}: 0;
  #{$y}: 0;
  #{$type}: 100%;

  transform-origin: #{$x} #{$y};

  @media (-webkit-min-device-pixel-ratio: 2) {
    #{$type}: 200%;
    transform: scale(0.5);
  }
  @media (-webkit-min-device-pixel-ratio: 3) {
    #{$type}: 300%;
    transform: scale(0.333);
  }
}

@mixin hairline-top($color) {
  position: relative;
  &:before {
    height: 0;
    border-top: 1px solid $color;
    @include _hairline(left, top, width);
  }
}

@mixin hairline-bottom($color) {
  position: relative;
  &:after {
    height: 0;
    border-bottom: 1px solid $color;
    @include _hairline(left, bottom, width);
  }
}

@mixin hairline-left($color) {
  position: relative;
  &:before {
    width: 0;
    border-left: 1px solid $color;
    @include _hairline(left, top, height);
  }
}

@mixin hairline-right($color) {
  position: relative;
  &:after {
    width: 0;
    border-right: 1px solid $color;
    @include _hairline(right, top, height);
  }
}

@mixin hairline-around($color: currentColor, $border-radius: 1px) {
  position: relative;
  &:after {
    width: 100%;
    border: 1px solid $color;
    border-radius: $border-radius;
    @media (-webkit-min-device-pixel-ratio: 2) {
      width: 200%;
      border-radius: $border-radius * 2;
    }
    @media (-webkit-min-device-pixel-ratio: 3) {
      width: 300%;
      border-radius: $border-radius * 3;
    }
    @include _hairline(left, top, height);
    bottom: -100%;
  }
}

使用:

image.png

四、iPhoneX 适配方案

iPhoneX 取消了物理按键,改用小黑条,这就导致出现了一些屏幕适配问题:

image.png

解决方案:env() 和 constant()

iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域和边界的距离,有四个预定义的变量:

  • safe-area-inset-left:安全区域距离左边边界距离;
  • safe-area-inset-right:安全区域距离右边边界距离;
  • safe-area-inset-top:安全区域距离顶部边界距离;
  • safe-area-inset-bottom:安全区域距离底部边界距离;
padding-bottom: constant(safe-area-inset-bottom);  /* 兼容 iOS < 11.2 */ 
padding-bottom: env(safe-area-inset-bottom);  /* 兼容 iOS >= 11.2 */

欢迎大家参与讨论,有问题随时指出~~

开心的豆丁.png