Bootstrap

移动端适配大总结

一、前言

大家都知道现在市面上的手机尺寸越来越多,对于一名前端开发来说,面临着开发上的挑战。要使各个手机上的UI达成一致效果,就需要我们采用各种方案规避差距。

二、rem方案

最常见的就是利用手淘的flexible.js,包含amfe-flexible和lib-flexible两种,
amfe-flexible是lib-flexible的升级版

2.1 实现原理

2.1.1 使用rem实现尺寸自适应

flexible的核心思想是利用rem单位,rem单位是基于html的font-size来做计算的,例如font-size为100px,那么200px就可以用2rem表示

// 在手淘里认为1rem = 屏幕尺寸 / 10
function setRem () {
    var rem = docEl.clientWidth / 10
    document.documentElement.style.fontSize = rem + 'px'
}
setRem ();

如上代码所示,Flexible将整个页面的宽度切成了10份,然后将计算出来的页面宽度的1/10设置为html节点的fontSize,也就意味着,页面上以 rem为单位的元素的尺寸 都是和 页面宽度 息息相关的

2.1.2 rem解决1px问题

设置viewport的width为device-width,改变浏览器viewport(布局视口和视觉视口)的默认宽度为理想视口宽度,从而使得用户可以在理想视口内看到完整的布局视口的内容。

等比设置viewport的initial-scale、maximum-scale、minimum-scale的值,从而实现1物理像素=1css像素,以适配高倍屏的显示效果(就是在这个地方规避了大家熟知的“1px问题”)

var metaEL= doc.querySelector('meta[name="viewport"]');
var dpr = window.devicePixelRatio;
var scale = 1 / dpr
metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

2.2 使用教程

2.2.1 使用flexible

项目前提是基于Vue Cli3.x的Webpack项目,flexible可以动态改变 根元素的 font-size

cnpm intall amfe-flexible -D // 安装amfe-flexible
import ‘amfe-flexible’ // 在 src\main.js 中引入 amfe-flexible

2.2.2 px2rem 配置

作用: 可以让根元素fontSize根据屏幕宽度自动变化

// px自动转rem
cnpm intall postcss-px2rem -D

在项目根目录下创建postcss.config.js文件

plugins: {
    ...,
    'postcss-pxtorem': {
        // 750设计标准,rootValue为设计图宽度/10
        rootValue: 75,
        // 转换成的rem后,保留小数点后几位
        unitPrecision: 5,
        /**
        * 将会被转换的css属性列表,
        * 设置为*表示全部,['*','*position*','!letter-spacing','!font*']
        * *position* 表示所有包含 position 的属性
        * !letter-spacing 表示非 letter-spacing 属性
        * !font* 表示非font-size font-weight ... 等的属性
        * */
        propList: ['*', '!letter-spacing'],
        // 不会被转换的class选择器名,支持正则
        selectorBlackList: ['.rem-'],
        replace: true,
        // 允许在媒体查询中转换`px`
        mediaQuery: false,
        // 小于1px的将不会被转换
        minPixelValue: 1
    }
}

2.3 现有问题

2.3.1 无法兼容安卓手机视网膜屏

市面上的安卓手机的dpr比较杂乱,1 到 4 甚至到 5,更甚者1.75、2.6、3.5,所以flexible放弃了对安卓手机多倍屏的适配,直接把dpr=1。

2.3.2 使用iframe引用也会出现问题。

iframe中的css单位还是px

三、viewport

viewport适配是现在 很主流更完美 的适配方案,兼容性也很不错

3.1 实现原理

我们都知道屏幕的宽度可以用 100vw 表示,所以对于 750px 的设计图来说, 100vw =750px,代表 1vw = 7.5px,在使用中直接用vw作为单位,就可以实现 容器和文字 的适配。

3.2 使用教程

3.2.1 安装px-to-viewport

主要用来把 px 单位转换为 vw、vh、vmin 或者 vmax 这样的视窗单位(推荐转换为 vw,其他单位兼容性问题比较多)。项目前提是基于Vue Cli3.x的Webpack项目。

cnpm install postcss-px-to-viewport -D

3.2.2 px-to-viewport配置

在项目根目录下创建postcss.config.js文件,px会自动转换为vw

module.exports = {
  plugins: {
    "postcss-px-to-viewport": {
      unitToConvert: "px", // 需要转换的单位,默认为"px"
      viewportWidth: 750, //  设计稿的视口宽度
      unitPrecision: 5, // 单位转换后保留的精度
      propList: ["*"], // 能转化为vw的属性列表
      viewportUnit: "vw", //  希望使用的视口单位
      fontViewportUnit: "vw", // 字体使用的视口单位
      selectorBlackList: [], // 需要忽略的CSS选择器
      minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
      mediaQuery: false, // 媒体查询里的单位是否需要转换单位
      replace: true, // 是否直接更换属性值,而不添加备用属性
      exclude: [], // 忽略某些文件夹下的文件或特定文件
      include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
      landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
      landscapeUnit: "vw", // 横屏时使用的单位
    },
    "postcss-preset-env": {
      browsers: "last 2 versions", //指定只对最近 2 个版本的浏览器进行兼容性处理。
    },
  },
};

3.3 存在问题

3.3.1 边框1px问题
  1. border-image
    安卓低端机和部分ios存在兼容性问题,而且无法实现圆角

  2. background-image
    background-image方案,在机型上都能比较好的展现,但是在背景图方案中需要提供2像素的图片。

  3. 渐变背景图
    虽然无法实现圆角,但使用率还是挺高的

  4. 伪类+transform
    我认为最值得灵活,最值得提倡的方案,既可以实现圆角,兼容性也好

border-1px($color = #ccc, $radius = 2PX, $direction = all)
  position: relative
  &::after
    content: ""
    pointer-events: none
    display: block
    position: absolute
    border-radius: $radius
    box-sizing border-box
    width 100%
    height 100%
    left: 0
    top: 0
    transform-origin: 0 0
    if $direction == all
      border: 1PX solid $color
    else
      border-{$direction}: 1PX solid $color
    @media only screen and (-webkit-min-device-pixel-ratio:2)
      width: 200%
      height: 200%
      border-radius: $radius * 2
      transform: scale(.5)
    @media only screen and (-webkit-min-device-pixel-ratio:3)
      width: 300%
      height: 300%
      border-radius: $radius * 3
      transform: scale(.333)
3.3.2 宽度大于100vw问题

如果 容器 用vw作为单位,margin用px的话,宽度容易超过100vw,出现滚动问题
解决方式可以用padding代替margin,或者用calc算margin

四 多倍屏图片问题

多倍屏的1px问题,Flexible默认就可以实现适配,viewport可以自己写伪类解决。但是无论是rem还是viewport,图片适配都需要单独处理。

4.1 媒体查询+预编译语言

直接写死不灵活,可以结合预编译语言实现

@mixin bg-image($url) {
    background-image: url($url + "@2x.png");
    @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3) {
        background-image: url($url + "@3x.png");
    }
}
.div{
  width:30px;
  height:20px;
  background-size:30px  20px;
  background-repeat:no-repeat;
  //在这里相当于调用了上面媒体查询的方法 ,传入图片url
  @include bg-image('special_1');     
}

4.2 image-set

仅支持背景图
.avatar {
    background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}

4.3 srcset

仅仅支持img图片

<img src="conardLi_1x.png"  srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">

4.4 svg

可伸缩矢量图,放大缩小都不会失真

参考大神知乎的文章
https://zhuanlan.zhihu.com/p/291013307

;