移动端适配问题解决方案

21,378 阅读7分钟

前言

随着时间的发展,现在基本上人手一部手机的低头族。做为前端开发的程序猿,在开发移动端web应用的时候,对面一堆各色尺寸不一样的屏幕,就有点淡淡的忧伤。

已上是2018年二月份最新的友盟数据,可在这里查看详情

很明显我们所要实现的就是在上述如此之多的屏幕,都能实现UI大大出的视觉图上的效果。而要实现这样的效果主要有两个难点

  1. 各屏幕适配
  2. Retina屏下的细节处理(主要是1px的问题)

各屏幕适配

各屏幕的适配,有两种方案,flexible + remvw。这三个单词是什么意思,看着很眼熟,但是这两个方案居然是什么呢,请允许我细细道来。

flexible + rem

显而易见,该方案是由rem 以及 flexible组成的。rem (font size of the root element)相对于根元素(即html元素)font-size计算值的倍数,flexible 即 flexible.js, 手淘团队提供的一个为该方案屏幕适配而写的一个库,主要实现的功能就是,根据屏幕的宽度给 html 元素设置一个合适的 font-size 值。

怎么样看的不是很懂是吧。让我来用一个页面场景再复述一遍。

正常情况下,我们的UI大大会以iphone6的尺寸为标准,做一套视觉效果图,并在上面进行标注,给到我们的标注图,如下所示

拿到这个图 我们该如何下手呢

  1. 先设置 html 元素的 font-size, 这个值我们暂时设置为 font-size: 37.5px,即1rem = 37.5px;
  2. 以iphone6为例子,其屏幕宽度为 750px, 整个屏幕的宽度即 20rem = 37.5 * 20px = 750px;
  3. 此时手机号的输入框为 490px = 13.06777777rem
  4. 依次将页面上的px转换为rem,这样我们就得到了全是rem为尺寸单位的页面

到这里为止是不是就结束了呢 ? 很遗憾的告诉你并不是。为什么 html 元素的 font-size 要设置为 37.5px呢?现在这个界面在iphone6上能完美显示了,在iphone5s,iphone6p能完美显示吗?(iphone6, iphone6s, iphone7. iphone8屏幕没有发生变化,本文直接用iphone6代替了。) 上面的两个问题 我们还有没解决,这个时候就轮到flexible登场了。只需要像如下引入就可实现用js来自动根据屏幕宽度设置 html元素的font-size的值。

<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>

引申一下

在上述过程中,你会发现,UI给到我们的一般是px标注的图,我们将其转化为rem,这个过程其实会花费很大的计算时间。做为一个合格的程序员,我们应该把这种机械性无脑的操作交给计算机来实现。我使用的是PostCss的插件 postcss-px2rem,这个插件能让我们在写代码时候直接写px,在构建的时候自动将我们所写的px转换为rem,大大提升了我们的开发效率。

vw

这个vw的方案,相当而言还比较新。vw 即(viewport width)可视窗口的宽度。之所以把这个方案放在后面来说,是因为viewport在去年(2017年)的时候存在不少的兼容性问题,各个浏览器的支持并不是很好。但是来到了2018年这个时间点,viewport单位意见得到了众多浏览器的支持(80.45%)。

可以在这里查看。

接下来就让我们来正式了解下这个方案吧。 vw既然是一个尺寸单位,那它的宽度等于多少呢?等于1%整个屏幕的宽度。举个例子,再次以iphone6手机为例,100vw = 750px => 1vw = 7.5px

再一次那上次的界面做示范

  1. 根据定义,我们了解了在iphone6手机上 1vw = 7.5px
  2. 此时手机号的输入框为 490px = 65.333333vw
  3. 依次将页面上的px转换为vw,这样我们就得到了全是vw为尺寸单位的页面

到这里为止是不是就结束了呢? 是的就是这么简单。

引申一下

跟之前一样的痛点,我们仍然需要花费大量不必要的计算时间去把标注图中的px转换为vw,有没有类似于postcss-px2rem的工具呢?很荣幸能再次站在巨人的肩膀上,已经有大神写了了类似的PostCss插件 postcss-px-to-viewport

1px问题

移动端的屏幕不仅仅分辨率有差异,其实还有Retina屏的问题。正常情况下,我们代码里的1px在屏幕上就应该显示一个像素点,但是在Retina屏下则不仅仅是一个像素点。再次拿iphone6为例,其dpr(device pixel ratio)设备像素比为2,css中一个1x1的点,其实在iphone6上是2x2的点,并且1px的边框在devicePixelRatio = 2的Retina屏下会显示成2px,在iPhone6 Plus下甚至会显示成3px。

这样的话,我们就会发现在有些手机上1px明显跟另外的一些手机的1px粗细不一样。 其实大多数的小公司不会扣这样的一个细节,比如说我们公司不会。(^__^) 嘻嘻……

但是作为一个有追求的前端工程师,我们应该尽量的把事情做的完美一点,(ps.像大公司看齐,在大公司这个细节问题其实是不容忽视的,为了自己以后的发展前途,我们有必要把这个细节完善掉的。)

这个问题的解决方案有很多,个人觉得最简单方面的还是大漠大大的一种解决方案。使用postcss-write-svg插件,

@svg 1px-border {
  height: 2px;
  @rect {
    fill: var(--color, black);
    width: 100%; height: 50%;
    }
  }
.example {
  border: 1px solid transparent;
  border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch;
}

编译出来就是

.example {
  border: 1px solid transparent;
  border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch;
}

其他

小程序中的屏幕适配

最近在写小程序,在小程序里,使用的是小程序的那套,跟平时用的vue全家桶以及react全家桶不一样,并没有配置webpack,在这种情况下我们使用postcss其实很困难(反正我是搞不出来/(ㄒoㄒ)/~~)

那该怎么办呢,小程序提供了一个自己的单位, rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

设备 rpx换算px (屏幕宽度/750) px换算rpx (750/屏幕宽度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6p 1rpx = 0.552px 1px = 1.81rpx

我们直接用拿到iphone6的标注图,直接写rpx就好。棒👍

总结

如果仔细看一下的话,会发现之前的rem的方案其实就是模拟了vw的实现方案,两者有非常多的相似之处。在lib-flexible的github上有着这样的一句话。

由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方案。vw的兼容方案可以参阅《如何在Vue项目中使用vw实现移动端适配》一文。

我们可以得到一个很明确的信息,lib-flexible这个方案已经被放弃使用了,我们可以去拥抱vw的那套实现方案。

本文参考了大漠大大在 www.w3cplus.com 里的多篇文章

  1. 使用Flexible实现手淘H5页面的终端适配
  2. 再聊移动端页面的适配
  3. 走向视网膜(Retina)的Web时代
  4. 再谈Retina下1px的解决方案
  5. 小程序-wxss