adapt.md 8.8 KB
Newer Older
W
wanganxp 已提交
1
## 屏幕适配指南
d-u-a's avatar
d-u-a 已提交
2

W
wanganxp 已提交
3
uni-app是以移动为先的理念诞生的。从uni-app 2.9起,开始优化PC等宽屏设备的配置。
d-u-a's avatar
d-u-a 已提交
4

W
wanganxp 已提交
5
PC适配和屏幕适配略有差异。PC适配包含`宽屏适配``uni-app内置组件适配PC`两方面的工作。
d-u-a's avatar
d-u-a 已提交
6

W
wanganxp 已提交
7
uni-app内置组件的PC适配,又包括`PC交互习惯的UI调整``非webkit浏览器适配`这两部分。这块工作不在本文的讨论范围内,尤其是开发者在PC端可以随意使用普通html元素和组件,不局限于uni-app内置组件。所以本文重点讨论屏幕适配。
d-u-a's avatar
d-u-a 已提交
8

W
wanganxp 已提交
9
uni-app提供的屏幕适配方案,包括3部分:
d-u-a's avatar
d-u-a 已提交
10

W
wanganxp 已提交
11 12
#### 1. 页面窗体级适配方案:leftWindow、rightWindow、topWindow
以目前手机屏幕为主window,在左右上,可新扩展 leftWindow、rightWindow、topWindow,这些区域可设定在一定屏幕宽度范围自动出现或消失。这些区域各自独立,切换页面支持在各自的window内刷新,而不是整屏刷新。
d-u-a's avatar
d-u-a 已提交
13

W
wanganxp 已提交
14
各个window之间可以交互通信。
d-u-a's avatar
d-u-a 已提交
15

W
wanganxp 已提交
16
这里有一个例子,分栏式的DCloud社区:[https://static-1afcc27f-ce2f-4a6d-9416-c65a6f87d24e.bspapp.com/#/](https://static-1afcc27f-ce2f-4a6d-9416-c65a6f87d24e.bspapp.com/#/)
d-u-a's avatar
d-u-a 已提交
17

W
wanganxp 已提交
18 19 20
该例子有如下特点:
- 在宽屏下会新增rightWindow区域,用于显示详情页面,点击左边的列表在右边显示详情内容。而窄屏下仍然是点击列表后新开一个页面显示详情内容。
- rightWindow里的页面是复用的,支持把已有详情页面当组件放到 rightWindow 页面中。
d-u-a's avatar
d-u-a 已提交
21

W
wanganxp 已提交
22
这套方案是已知的、最便捷的分栏式宽屏应用适配方案。
d-u-a's avatar
d-u-a 已提交
23

W
wanganxp 已提交
24
leftWindow方案尤其适用于分栏式应用和PC Admin管理控制台。
d-u-a's avatar
d-u-a 已提交
25

W
wanganxp 已提交
26
目前的leftWindow、rightWindow、topWindow 只支持H5端。计划后续在Pad App上实现该配置。小程序无法支持该配置。
d-u-a's avatar
d-u-a 已提交
27 28 29 30 31 32

pages.json 配置

```json
{
  "globalStyle": {
W
wanganxp 已提交
33
    
d-u-a's avatar
d-u-a 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
  },
  "topWindow": {
    "path": "responsive/top-window.vue", // 指定 topWindow 页面文件
    "style": {
      "height": "44px"
    }
  },
  "leftWindow": {
    "path": "responsive/left-window.vue", // 指定 leftWindow 页面文件
    "style": {
      "width": 300
    }
  },
  "rightWindow": {
    "path": "responsive/right-window.vue", // 指定 rightWindow 页面文件
    "style": {
      "width": "calc(100vw - 400px)" // 页面宽度
    },
    "matchMedia": {
      "minWidth": 768 //生效条件,当窗口宽度大于768px时显示
    }
  }
}
```

W
wanganxp 已提交
59 60 61 62 63 64 65
#### 2. 组件级适配方案:match-media组件

leftWindow是页面窗体级适配方案。而在同一个页面中,仍然可以使用组件级适配方案。

uni-app提供了 [match-media组件](https://uniapp.dcloud.net.cn/component/match-media) 和配套的 [uni.createMediaQueryObserver](https://uniapp.dcloud.net.cn/api/ui/media-query-observer) 方法。

这是一个媒体查询适配组件,可以更简单的用于动态屏幕适配。
d-u-a's avatar
d-u-a 已提交
66

W
wanganxp 已提交
67
`match-media`组件中放置内容,并为组件指定一组 media query 媒体查询规则,如屏幕宽度。运行时,如屏幕宽度满足查询条件,这个组件就会被展示,反之则隐藏。
d-u-a's avatar
d-u-a 已提交
68

W
wanganxp 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
`match-media`组件的优势包括:
1. 开发者能够更方便、显式地使用 Media Query 能力,而不是耦合在 CSS 文件中,难以复用。
2. 能够在模板中结合数据绑定动态地使用,不仅能做到组件的显示或隐藏,在过程式 API 中可塑性更高,例如能够根据尺寸变化动态地添加 class 类名,改变样式。
3. 能够嵌套式地使用 Media Query 组件,即能够满足局部组件布局样式的改变。
4. 组件化之后,封装性更强,能够隔离样式、模版以及绑定在模版上的交互事件,还能够提供更高的可复用性。

它的详细文档参考:[https://uniapp.dcloud.net.cn/component/match-media](https://uniapp.dcloud.net.cn/component/match-media)

当然,开发者也可以继续使用css媒体查询来适配屏幕。

uni-app的屏幕适配推荐方案是运行时动态适配,而不是为PC版单独条件编译(虽然您也可以通过自定义条件编译来实现单独的PC版)。这样设计的好处是在ipad等设备的浏览器上可以方便的横竖屏切换。

#### rpx单位的处理

屏幕适配其实有3种策略:
1. 指定屏幕范围内显示隐藏内容。前述的 leftWindow 和 match-media组件都属于这类解决方案。
2. 页面内容划分为固定区域和长宽动态适配区域,固定区域使用固定的px单位约定宽高,长宽适配区域则使用flex自动适配。当屏幕大小变化时,长宽适配区域跟着变化
3. 根据页面屏幕宽度缩放。rpx其实属于这种类型。在宽屏上,rpx变大,窄屏上rpx变小。

在移动设备上,屏幕碎片化也很严重,设计师一般以按照750px屏幕宽度出图。此时使用rpx的好处在于,各种移动设备的屏幕宽度差异不是很大,相对于750px微调缩放后的效果,尽可能的还原了设计师的设计。

但是,一旦脱离移动设备,在pc屏幕,或者pad横屏状态下,因为屏幕宽度远大于750了。此时rpx根据屏幕宽度变化的结果就严重脱离了预期,大的惨不忍睹。
d-u-a's avatar
d-u-a 已提交
91

W
wanganxp 已提交
92 93 94 95 96
为此,在uni-app 2.9+起,新增了 rpx 的生效范围,并且将 rpx 的默认最大适配宽度设为了 960 px。

也就是设计师按750px出具的设计图,可适配的最大屏幕宽度为960px,在这个范围内,rpx可以根据屏幕宽度缩放。一旦超过960,rpx再根据屏幕宽度缩放就变的没有意义了。按如下配置,在超过960宽的屏幕上,会按375px作为基准宽度,这是最大程度上保持界面不失真的策略。

当然这些配置您都可以自己定义调整,在 pages.json 的 globeStyle 里配置 rpx 的如下参数。
d-u-a's avatar
d-u-a 已提交
97 98 99 100 101 102 103 104 105 106 107

```json
{
  "globalStyle": {
    "rpxCalcMaxDeviceWidth": 960, // rpx 计算所支持的最大设备宽度,单位 px,默认值为 960
    "rpxCalcBaseDeviceWidth": 375, // rpx 计算使用的基准设备宽度,设备实际宽度超出 rpx 计算所支持的最大设备宽度时将按基准宽度计算,单位 px,默认值为 375
    "rpxCalcIncludeWidth": 750 // rpx 计算特殊处理的值,始终按实际的设备宽度计算,单位 rpx,默认值为 750
  },
}
```

W
wanganxp 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
如果您的代码中把750rpx当做100%来使用(官方强烈不推荐这种写法,即便是nvue不支持百分比,也应该使用flex来解决撑满问题),此时不管屏幕宽度为多少,哪怕超过了960px,您的预期仍然是要占满整个屏幕宽度,但如果按rpxCalcBaseDeviceWidth的375px的策略执行将不再占满屏宽。

此时您有两种解决方案,一种是修改代码,将里面把rpx当做百分比的代码改掉;另一种是配置rpxCalcIncludeWidth,设置某个特定数值不受rpxCalcMaxDeviceWidth约束。如上述例子中的"rpxCalcIncludeWidth": 750,代表着如果写了 750rpx,始终将按屏幕宽度百分百占满来计算。

- 关于 rpx 转 px

不少开发者之前对rpx的使用过于没有节制,后来为了适配宽屏,想要改用“页面内容划分为固定区域和长宽动态适配区域”的策略,此时将回归px。

比如[hello uni-app社区的宽屏适配示例](https://static-1afcc27f-ce2f-4a6d-9416-c65a6f87d24e.bspapp.com/#/)就没有使用rpx。

如果想把rpx转px,可以在源码里正则替换,也可以使用三方已经写好的单位转换库。

项目根目录新增文件 `postcss.config.js`,内容如下。则在编译时,编译器会自动转换rpx单位为px。

** 注意:将rpx作为百分比的用法需要手动处理
d-u-a's avatar
d-u-a 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

```js
// postcss.config.js

const path = require('path')
module.exports = {
  parser: 'postcss-comment',
  plugins: {
    'postcss-import': {
      resolve(id, basedir, importOptions) {
        if (id.startsWith('~@/')) {
          return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
        } else if (id.startsWith('@/')) {
          return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
        } else if (id.startsWith('/') && !id.startsWith('//')) {
          return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
        }
        return id
      }
    },
    'autoprefixer': {
      overrideBrowserslist: ["Android >= 4", "ios >= 8"],
      remove: process.env.UNI_PLATFORM !== 'h5'
    },
    // 借助postcss-px-to-viewport插件,实现rpx转px,文档:https://github.com/evrone/postcss-px-to-viewport/blob/master/README_CN.md
    // 以下配置,可以将rpx转换为1/2的px,如20rpx=10px,如果要调整比例,可以调整 viewportWidth 来实现
    'postcss-px-to-viewport': {
      unitToConvert: 'rpx',
      viewportWidth: 200,
      unitPrecision: 5,
      propList: ['*'],
      viewportUnit: 'px',
      fontViewportUnit: 'px',
      selectorBlackList: [],
      minPixelValue: 1,
      mediaQuery: false,
      replace: true,
      exclude: undefined,
      include: undefined,
      landscape: false
    },
    '@dcloudio/vue-cli-plugin-uni/packages/postcss': {}
  }
}
```
W
wanganxp 已提交
168 169