要怎么做 CSS 适配呢
前言
CSS 适配的目的就是让设计稿在不同屏幕宽度下的视觉效果尽可能一致,相关技术也是本文讨论的内容。
预备知识
理想视口
通常视口(Viewport)是指,浏览器用来显示网页的那部分区域。在移动端开发中,我们希望页面宽度和设备宽度一致,并把这个想视口称为理想视口(ideal viewport)。可以通过 HTML 中的<meta>设置理想视口:
html- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
width=device-width就是视口宽度等于内容宽度,initial-scale、maximum-scale、minimum-scale、user-scalable,分别是页面初始放缩倍数、最大放缩倍数、最小防缩倍数,以及是否允许用户放缩。
设备像素比
首先有几个常见的概念:
物理像素(Physical Pixel)是手机屏幕上显示的最小单元,该最小单元具有颜色及亮度的属性可供设置,像素分辨率一般是指的物理像素,例如:iPhone 6 plus:1242*2208、iPhone 6:750*1334、iPhone 5s:640*1136。
设备独立像素(Density-Indenpendent Pixel),或者说逻辑像素,是计算机设备中的一个点,CSS 中设置的 px 指的就是该像素。手机的逻辑分辨率一般是指逻辑像素,例如:iPhone 6 plus:414*736、iPhone 6:375*667、iPhone 5s 320*568。
设备像素比(Device Pixel Ratio,DPR),有的地方也说倍率,就是物理像素和设备独立像素之比。
以 iPhone 6 为例,,一个逻辑像素(CSS 里面的 1px)包含了 2*2 = 4 个物理像素。一般情况下,两个图形逻辑像素的尺寸相同,就是看起来一样大。那 DPR 高的好处就是,在一个逻辑像素里面可以展示更多的细节,针对高分辨率的手机提供 2x 图就是源于此。
那对于前端移动端适配有什么影响吗?其实并没有,因为物理像素对 CSS 来说是透明的,我们不需要关心,我们只需要让 UI 尺寸呈现相同的效果就行了。
移动端适配
样式的适配需要考虑到不同屏幕尺寸对 UI 大小的影响,这方面的方法一般有 rem 布局、viewport 布局、媒体查询等。
rem 布局
什么是 rem 布局
rem 是一个与页面根元素<html>的字体大小font-size相等的一个长度单位。如果根元素的字体大小是 12px,那么,在任何下属的标签下,1rem 就等于 12px 。移动端适配可以根据设计稿的宽度假设一个“标准单位”,也就是前端代码的 1rem 的宽度,例如 。然后把 UI 的尺寸,转换为相对于设计稿宽度的尺寸,例如,设计稿宽度 750px,有 ,一个宽度 90px 的按钮,宽度就变成了 1.44rem。在页面上,根据用户屏幕宽度重新设置 rem 的大小,以 rem 为长度单位的 UI 也会等比例的放缩,从而实现移动端适配。
淘宝网页手机版的 CSS 适配就是基于 rem 布局的。
postcss-pxtorem
很显然,这样子的单位转换非常繁琐,但是我们有 PostCSS。postcss-pxtorem 是一个 PostCSS 的插件,可以自动把 px 单位转为 rem 单位,也就是说,开发的时候直接按照照抄设计稿的 px 单位的尺寸就可以了。
安装:
cmd- npm install postcss-pxtorem
配置:
js- import { defineConfig } from 'vite'
- import px2rem from 'postcss-pxtorem'
- export default defineConfig({
- // ...
- css:{
- postcss:{
- plugins:[
- px2rem({
- rootValue: 75, // 根元素字体大小,并且设计稿宽度视为 rootValue * 10
- unitPrecision: 4, // rem 转换后保留小数位数
- propList: ['*'], // 允许被转换成 rem 的 css 属性值,具体规则见文档
- selectorBlackList: [], // 要忽略并保留为px的选择器,支持正则和字符串匹配
- mediaQuery: false, // 允许在媒体查询中转换px
- minPixelValue: 1 // 设置要替换的最小像素值
- exclude: /node_modules/, // 需要排除的文件路径,支持正则、函数和字符串匹配
- })
- ]
- }
- }
- })
如果使用了例如 Vant(设计稿宽度 375px)等第三方 UI 组件库,组件库设计稿宽度和项目设计稿宽度不同的话,我们可以这样:
js- px2rem({
- rootValue({ file }) {
- return file.indexOf('vant') !== -1 ? 37.5 : 75 // 假设项目设计稿是 750px 宽度的
- },
- // ...
- })
viewport 布局
什么是 viewport 布局
Viewport 布局即是使用 vw、vh 作为样式单位。1vw 等于 1% 的视口宽度,1vh 就是 1% 的视口高度,Viewport 布局即用 vw 来转换设计稿的尺寸单位,例如宽 750px 的设计稿,75px 就是 10vw。另外,还有 vmin、vmax 的样式单位,分别为 vw 和 vh 的最大值与最小值。
例如 Bilibili 手机版就使用了 viewport 布局,使用的是 vmin 的单位。
postcss-px-to-viewport
postcss-px-to-viewport 是一个将px单位转换为视口单位(一般就是vw)的 PostCSS 插件。开发中同样直接用设计稿的 px 的尺寸即可。
安装:
cmd- npm install postcss-px-to-viewport
配置:
js- import { defineConfig } from 'vite'
- import px2vw from 'postcss-px-to-viewport'
- export default defineConfig({
- // ...
- css: {
- postcss: {
- plugins: [
- px2vw({
- unitToConvert: 'px', // 要转化的单位
- viewportWidth: 750, // UI设计稿的宽度
- unitPrecision: 6, // 转换后的精度,即小数点位数
- propList: ['*'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
- viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
- fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
- selectorBlackList: ['ignore-'], // 指定不转换为视窗单位的类名,
- minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
- mediaQuery: false, // 是否在媒体查询的css代码中也进行转换,默认false
- replace: true, // 是否转换后直接更换属性值
- exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
- exclude: [],
- landscape: false // 是否处理横屏情况
- })
- ]
- }
- }
- })
另外如果要兼容第三方组件库,例如 Vant、AntD 之类的,可以起两个 postcss-xp-to-viewpoint 插件,一个把相应组件库的文件写exclude里面,适配设计稿,另一个用来适配组件库:
js- plugins: [
- px2vw({
- viewportWidth: 1920,
- viewportUnit: 'vw',
- exclude:[/node_modules\/antd/i]
- }),
- px2vw({
- viewportWidth: 1440,
- viewportUnit: 'vw',
- exclude: [/^(?!.*node_modules\/antd)/] //忽略非antd
- })
- ]
响应式布局
通过媒体查询,可以针对不同的屏幕进行单独设置样式:
css- .list {
- width: 50%;
- }
- // 针对小屏手机
- @media screen and (min-width: 576px) {
- .list {
- width: 100%;
- }
- }
响应式布局的思路由来已久,例如著名 UI 库 Bootstrap,但是使用响应式布局适配不同宽度的屏幕显然太麻烦的。其实实践中的做法是,设计会出手机、桌面的两张设计图,使用媒体查询来写两套不同的样式,不同宽度的手机或者桌面内部的样式就用 Rem 或者 Viewport 布局进行。
其他
一个项目里面既有移动端的,也有桌面端的页面,要怎么适配呢
像上面说的,使用媒体查询来写两套不同的样式,不同宽度的手机或者桌面内部的样式就用 Rem 或者 Viewport 布局进行。
代码层面的话,个人感觉可以把桌面端和手机端的 CSS 文件分别命名为xx.desktop.css和xx.mobile.css,然后在 postcss-px-to-viewport 或者 postcss-pxtorem 中根据文件名来设置不同的设计稿宽度。
结尾
本文稍微浅浅地总结了一下现在移动端适配的常用做法,喵~
