移动端适配常见问题

209 阅读5分钟

前言

移动端适配在开发中经常遇到,这里做了如下汇总:

一、1px物理像素线

1、实现普通屏幕下1px,高清屏幕下0.5px,伪类+采用transform属性scale实现

.mod_grid {
    position: relative;
    &::after {
        // 实现1物理像素的下边框线
        content: '';
        position: absolute;
        z-index: 1;
        pointer-events: none;
        background-color: #ddd;
        height: 1px;
        left: 0;
        right: 0;
        top: 0;
        @media only screen and (-webkit-min-device-pixel-ratio: 2) {
            -webkit-transform: scaleY(0.5);
            -webkit-transform-origin: 50% 0%;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .border_1px:before{
                transform: scaleY(0.33);
                -webkit-transform-origin: 33.333% 0%;
            }
        }

    }
    ...
}

2、border-image

基于media查询判断不同的设备像素比给定不同的border-image

       .border_1px{
          border-bottom: 1px solid #000;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .border_1px{
                border-bottom: none;
                border-width: 0 0 1px 0;
                border-image: url(../img/1pxline.png) 0 0 2 0 stretch;
            }
        }

3、background-image

border-image类似,准备一张符合条件的边框背景图,模拟在背景上。

       .border_1px{
          border-bottom: 1px solid #000;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .border_1px{
                background: url(../img/1pxline.png) repeat-x left bottom;
                background-size: 100% 1px;
            }
        }

上面两种都需要单独准备图片,而且圆角不是很好处理,但是可以应对大部分场景。

4、svg

上面我们border-imagebackground-image都可以模拟1px边框,但是使用的都是位图,还需要外部引入。 借助PostCSSpostcss-write-svg我们能直接使用border-imagebackground-image创建svg1px边框:

@svg border_1px { 
  height: 2px; 
  @rect { 
    fill: var(--color, black); 
    width: 100%; 
    height: 50%; 
    } 
  } 
.example { border: 1px solid transparent; border-image: svg(border_1px 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; }

二、移动端px转vw适配方案(vue+vant)

1、安装插件

npm install postcss-px-to-viewport --save-dev

2、项目根目录添加配置文件

postcss.config.js

3、添加配置

const path = require('path');
 
module.exports = ({ file }) => {
  const designWidth = file.dirname.includes(path.join('node_modules', 'vant')) ? 375 : 750;
 
  return {
    plugins: {
      autoprefixer: {},
      "postcss-px-to-viewport": {
        unitToConvert: "px", // 需要转换的单位,默认为"px"
        viewportWidth: designWidth,  // 设计稿的视口宽度
        unitPrecision: 6, // 单位转换后保留的精度
        propList: ["*"],  // 能转化为vw的属性列表
        viewportUnit: "vw", // 希望使用的视口单位
        fontViewportUnit: "vw", // 字体使用的视口单位
        selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
        minPixelValue: 1, //设置最小的转换数值,如果为1的话,只有大于1的值会被转换
        mediaQuery: true, // 媒体查询里的单位是否需要转换单位
        exclude: /(\/|\\)(node_modules)(\/|\\)/,  // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
        landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
        landscapeUnit: 'vw', // 横屏时使用的单位 
        landscapeWidth: 1920 // 横屏时使用的视口宽度
      }
    }
  }
}

三、适配iphoneX

现在很多手机也出现了一些圆角(corners)、刘海(sensor housing)和小黑条(Home Indicator)为了适配这些手机,安全区域这个概念变诞生了:安全区域就是一个不受上面三个效果的可视窗口范围。

1、viewport-fit

viewport-fit是专门为了适配iPhoneX而诞生的一个属性,它用于限制网页如何在安全区域内进行展示。

image.png contain: 可视窗口完全包含网页内容

cover:网页内容完全覆盖可视窗口

默认情况下或者设置为autocontain效果相同。

2、env、constant

image.png 我们需要将顶部和底部合理的摆放在安全区域内,iOS11新增了两个CSS函数env、constant,用于设定安全区域与边界的距离。

函数内部可以是四个常量:

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离

注意:我们必须指定viweport-fit后才能使用这两个函数

<meta name="viewport" content="viewport-fit=cover">

constantiOS < 11.2的版本中生效,enviOS >= 11.2的版本中生效,这意味着我们往往要同时设置他们,将页面限制在安全区域内:

body {
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}

当使用底部固定导航栏时,我们要为他们设置padding值:


padding-bottom: constant(safe-area-inset-bottom); 
padding-bottom: env(safe-area-inset-bottom); 

四、横屏适配

image.png 很多视口我们要对横屏和竖屏显示不同的布局,所以我们需要检测在不同的场景下给定不同的样式:

1、JavaScript检测横屏

window.orientation:获取屏幕旋转方向

window.addEventListener("resize", ()=>{
    if (window.orientation === 180 || window.orientation === 0) { 
      // 正常方向或屏幕旋转180度
        console.log('竖屏');
    };
    if (window.orientation === 90 || window.orientation === -90 ){ 
       // 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
        console.log('横屏');
    }  
}); 

2、CSS检测横屏

@media screen and (orientation: portrait) {
  /*竖屏...*/
} 
@media screen and (orientation: landscape) {
  /*横屏...*/
}

五、图片模糊问题

为了保证图片质量,我们应该尽可能让一个屏幕像素来渲染一个图片像素,所以,针对不同DPR的屏幕,我们需要展示不同分辨率的图片。

如:在dpr=2的屏幕上展示两倍图(@2x),在dpr=3的屏幕上展示三倍图(@3x)。 使用media查询判断不同的设备像素比来显示不同精度的图片:

1、media查询

       .avatar{
            background-image: url(conardLi_1x.png);
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .avatar{
                background-image: url(conardLi_2x.png);
            }
        }
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .avatar{
                background-image: url(conardLi_3x.png);
            }
        }

 2、image-set

.avatar {
    background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}

3、srcset

使用img标签的srcset属性,浏览器会自动根据像素密度匹配最佳显示图片:

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

4、JavaScript拼接图片url

const dpr = window.devicePixelRatio;
const images =  document.querySelectorAll('img');
images.forEach((img)=>{
  img.src.replace(".", `@${dpr}x.`);
})

5、使用svg

SVG 的全称是可缩放矢量图(Scalable Vector Graphics)。不同于位图的基于像素,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。 除了我们手动在代码中绘制svg,我们还可以像使用位图一样使用svg图片:

<img src="conardLi.svg">

<img src="data:image/svg+xml;base64,[data]">

.avatar {
  background: url(conardLi.svg);
}

参考: juejin.cn/post/684490…