PC端/移动端适配方案

5,323 阅读9分钟

前端做网页, 基本都要做页面的适配,特别在做移动端的时候, 有时候移动端的网页在PC端也要打开,这时候就得同时做适配.

以下的方案是我在开发中所使用的,仅供参考

公共配置

调整meta标签

<meta
   name="viewport"
   content="width=device-width, 
   initial-scale=1.0, 
   maximum-scale=1.0, 
   minimum-scale=1.0, 
   user-scalable=no,
   viewport-fit=cover"
 />
  • width 宽度设置的是ve=iewport,可以设置device-width特殊值
  • initial-scale 初始缩放比,大于0的数字
  • maximum-scale 最大缩放比,大于0的数字
  • minimum-scale 最小缩放比,大于0的数字
  • user-scalable 用户是否可以缩放,yes或no(1或0)
  • viewport-fit:cover,页面内容充满屏幕, 默认contain

移动端特殊样式调整

*:not(input,textarea) {
    //禁止长按页面时的弹出菜单
    -webkit-touch-callout:none !important; 
    // 禁止文本选择
    -webkit-user-select:none !important;
    -moz-user-select:none !important;
    -ms-user-select:none !important; 
    user-select:none !important;
    //禁用元素上的所有手势
    // touch-action: none !important;
    //控制一个元素能否响应鼠标操作
    // pointer-events: none !important;
}

IOS安全区域适配

对于 iPhone 8 和以往的 iPhone,由于屏幕规规整整的矩形,安全区就是整块屏幕。但自从苹果手机 iphoneX 发布之后,前端人员在开发移动端Web页面时,得多注意一个对 IOS 所谓安全区域范围的适配。这其实说白了就是 iphoneX 之后的苹果手机,在页面上,你需要对顶部和底部多预留一点空间。造成这个问题的主要原因就是苹果手机在屏幕上出现了所谓的刘海屏,而且更是在屏幕下方加了一条小黑条。

1.png 手机纵向时,安全区上沿是从屏幕最顶端往下 44 pt,所以,安全区并不是和「齐刘海」完全齐平,而是要再往下一点点。

「下巴」位置上,为了给 Home 指示条足够的空间,从下往上推 34 pt 开始才被视为安全区。

把屏幕横过来的时候,「安全区」又产生了变化:Home 指示条挪到了屏幕下方,而「留海」和「下巴」的安全范围保持不变。也就是说,横向的安全区被“压缩”得更狭长了。

2.png

对于指示条位移但纵向安全区不变,官方给出的解释是:

用户在使用手机时非常依赖肌肉记忆(比如你可以不看键盘打字),不对称的设计尽管可以更有效地利用屏幕,但却与用户肌肉记忆相悖,容易引起误操作。水平布局时,交互元素两侧距离相等,左侧右侧旋转时位置固定,可以方便用户记忆。

3.png

绿色区域是安全区域,蓝色是margin

CSS 在 iOS11 新增特性中,Webkit 包含了两个新的函数 env() 和 constant(),以及四个预定义的常量:

  • safe-area-inset-top:安全区域距离顶部边界距离,状态栏+导航栏大约为88px。
  • safe-area-inset-left:安全区域距离左边边界距离,竖屏时为0。
  • safe-area-inset-right:安全区域距离右边边界距离,竖屏时为0。
  • safe-area-inset-bottom:安全区域距离底部边界距离,大约高度为34px。
body{
    /* 兼容 iOS < 11.2 */
    padding-top: constant(safe-area-inset-top);
    padding-left: constant(safe-area-inset-left);
    padding-right: constant(safe-area-inset-right);
    padding-bottom: constant(safe-area-inset-bottom);
    /* 兼容 iOS >= 11.2 */
    padding-top: env(safe-area-inset-top);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
    padding-bottom: env(safe-area-inset-bottom);
}

JS 当然,有时候单纯的 CSS 方式并不能满足实际的需求场景,那么我们就需要判断出那些 IOS 的手机屏幕是需要特殊处理的。

而 iphoneX 之后的苹果手机 Series 参数如下:

机型屏幕尺寸倍率设备分辨率(px)逻辑像素(pt)
4(s)3.51x640x960320x480
5(s/se)42x640x1136320x568
6(s)/7/84.72x750x1334375x667
6(s)/7/8 Plus5.53x1242x2208414x736
X/Xs/11 Pro5.83x1125x2436375x812
Xr/116.12x828×1792414x896
Xs Max/11 Pro Max6.53x1242×2688414x896
12/13 mini5.43x1080x2340375x812
12/13/146.13x1170x2532390x844
12/13 Pro Max / 14 Plus6.73x1284x2778428x926
14/15 Pro6.13x1179x2556393x852
14/15 Pro Max / 15 Plus6.73x1290x2796430x932
/** * 
判断IOS异形屏 
* @returns 
*/ 
export const isIphoneX = ():boolean => { 
    if (typeof window !== 'undefined' && window) { 
        var faultTolerantVal = 10; // 容错值 
        return /iphone/gi.test(window.navigator.userAgent) && (window.screen.height + faultTolerantVal) >= 812; 
        } 
     return false; 
 };

注意:设计图是以1080px的宽度设置

方案一: 基于postcss-px-to-viewport的vw 布局 (仅适用于移动端)

vw是一种视窗单位,也是相对单位。相对于视口的宽度,视口被均分为100单位的vw。能够实现页面内的字体大小跟随视口的大小而改变。

方案优点: 仅配置一次,后期不需要调整,在开发中样式单位直接使用px

方案缺点: 不适配PC

  • 1、安装postcss-px-to-viewport
yarn add postcss-px-to-viewport -D
npm i postcss-px-to-viewport -D
  • 2、postcss-px-to-viewport 在项目根目录中新建postcss.config.js
module.exports = () => {
  return {
    plugins: {
      'postcss-px-to-viewport': {
        unitToConvert: 'px', // 默认值`px`,需要转换的单位
        viewportWidth: 1080, // 视窗的宽度,对应的是我们设计稿的宽度
        viewportHeight: 1920, // 视窗的高度,可以不配置
        unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数,默认是5(很多时候无法整除)
        viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
        fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw;
        selectorBlackList: ['.ignore'], // 指定不转换为视窗单位的类
        minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位
        mediaQuery: true, // 允许在媒体查询中转换`px`,默认false
        exclude: [/node_modules/, /public/] // 如果是regexp, 忽略全部匹配文件;如果是数组array, 忽略指定文件 正则表达式,匹配的内容是不想被转换单位的文件名,添加了这种匹配后凡是文件名中带有这些字符的全部排除宽度
      }
    }
  }
}

方案二: 基于postcss-pxtorem的rem布局 (可同时适配PC端与移动端)

rem布局,代表在浏览器中看到的单位会从px转换为rem

方案优点: 仅配置一次,后期不需要调整,在开发中样式单位直接使用px

具体配置方式如下:

  • 1、安装postcss-pxtorem
yarn add postcss-pxtorem -D
npm i postcss-pxtorem -D
  • 2、配置postcss-pxtorem 在项目根目录中新建postcss.config.js
module.exports = () => {
  return {
    plugins: {
      'postcss-pxtorem': {
        rootValue: 100, //表示根元素字体大小或根据输入参数返回根元素字体大小
        unitPrecision: 5, //精确到多少位。
        propList: ['*'], //表示允许从px转换为rem的样式
        selectorBlackList: ['layout'], //表示这个类不允许把px转换为rem
        replace: false, //作用暂时没发现
        mediaQuery: false, //允许在媒体查询中转换px
        minPixelValue: 0, //设置要替换的最小像素值
        exclude: [/node_modules/i, /public/] //表示目录中的样式不参与转换
      }
    }
  }
}
  • 3、配置根标签
//判断pc端还是移动端
const validPcOrPhone = ():string => {
     if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
    return 'phone';
  }
  return 'pc';
}
//设置根标签
const autoFont = () => {
  const fontBase = 100
  const designWidth = 1080
  const html = document.querySelector('html') as HTMLElement
  let currentWidth = 0
  //判断是pc端,就设置固定宽度为750,可自行调整
  if (validPcOrPhone() === 'pc') {
    currentWidth = 750
  } else {
    currentWidth = html.clientWidth
  }
  const currentHeight = html.clientHeight
  if (currentWidth > currentHeight) {
    currentWidth = currentHeight
  }
  const currentFontWidth = (fontBase * currentWidth) / designWidth
  html.style.fontSize = currentFontWidth + 'px'
}
export default autoFont
  • 4、页面的使用(已Vue3举例,react同理)
<script lang="ts" setup>
import { validPcOrPhone,autoFont } from '@/utils'
import { ref } from 'vue'
const isPc = ref<boolean>(validPcOrPhone() === 'pc')
autoFont()
window.onresize = function () {
    autoFont()
    isPc.value = validPcOrPhone() === 'pc'
}
</script>
<template>
  <section :class="{ layout: isPc }">
    <router-view />
  </section>
</template>
<style lang="scss">
#app {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}
.layout {
  width: 750px;
  margin: 0 auto;
}
</style>

方案三: 基于postcss-px-to-viewport的rem布局 (可同时适配PC端与移动端)

rem布局,代表在浏览器中看到的单位会从px转换为rem。 方案优点: 仅配置一次,后期不需要调整,在开发中样式单位直接使用px

  • 1、安装postcss-px-to-viewport
yarn add postcss-px-to-viewport -D
npm i postcss-px-to-viewport -D
  • 2、postcss-px-to-viewport 在项目根目录中新建postcss.config.js
module.exports = () => {
  return {
    plugins: {
      'postcss-px-to-viewport': {
        unitToConvert: 'px', // 默认值`px`,需要转换的单位
        viewportWidth: 10800, // 视窗的宽度,对应的是我们设计稿的宽度 750*1334是iphone6的宽高
        viewportHeight: 19200, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
        unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数,默认是5(很多时候无法整除)
        viewportUnit: 'rem', // 指定需要转换成的视窗单位,建议使用vw
        fontViewportUnit: 'rem', // 指定字体需要转换成的视窗单位,默认vw;
        selectorBlackList: ['layout'], // 指定不转换为视窗单位的类
        minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位
        mediaQuery: true, // 允许在媒体查询中转换`px`,默认false
        exclude: [/node_modules/, /public/] // 如果是regexp, 忽略全部匹配文件;如果是数组array, 忽略指定文件 正则表达式,匹配的内容是不想被转换单位的文件名,添加了这种匹配后凡是文件名中带有这些字符的全部排除宽度
      }
    }
  }
}
  • 3、配置根标签
//判断pc端还是移动端
const validPcOrPhone = ():string => {
     if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
    return 'phone';
  }
  return 'pc';
}
//设置根标签
const autoFont = () => {
  const fontBase = 100
  const designWidth = 1080
  const html = document.querySelector('html') as HTMLElement
  let currentWidth = 0
  //判断是pc端,就设置固定宽度为750,可自行调整
  if (validPcOrPhone() === 'pc') {
    currentWidth = 750
  } else {
    currentWidth = html.clientWidth
  }
  const currentHeight = html.clientHeight
  if (currentWidth > currentHeight) {
    currentWidth = currentHeight
  }
  const currentFontWidth = (fontBase * currentWidth) / designWidth
  html.style.fontSize = currentFontWidth + 'px'
}
export default autoFont
  • 4、页面的使用(已Vue3举例,react同理)
<script lang="ts" setup>
import { validPcOrPhone,autoFont } from '@/utils'
import { ref } from 'vue'
const isPc = ref<boolean>(validPcOrPhone() === 'pc')
autoFont()
window.onresize = function () {
    autoFont()
    isPc.value = validPcOrPhone() === 'pc'
}
</script>
<template>
  <section :class="{ layout: isPc }">
    <router-view />
  </section>
</template>
<style lang="scss">
#app {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}
.layout {
  width: 750px;
  margin: 0 auto;
}
</style>