本文将从以下两个方面去讲解:
1,移动端为什么会有适配的问题
2,怎么解决的
移动端为什么会有适配的问题
不同型号的手机可能它的宽度、高度是不相同的,我们先来看懂这个表格,其中和前端相关的dp和px比较有意义,其他了解就行。
拿iPhone 13 Pro来说,它的对角线是6.1英寸(15.494厘米),横向有1170个栅格(物理像素px),纵向有2532个栅格(一个栅格表示1px),斜对角线上每英寸包含的栅格数是460PPI。
它的宽度是2.82英寸,横向每英寸包含的栅格数是1170/2.82=414.89,横向屏幕密度 dpi = 414.89/160=2.59约等于3.0;
它的高度是5.4英寸,纵向每英寸包含的栅格数是2532/5.4=468.9,纵向屏幕密度dpi = 468.9/160=2.93约等于3.0;
Tips:栅格=物理像素(physical pixel),单位px,不等于css的px
Tips:手机屏幕最小单位是物理硬件栅格,每个手机的栅格大小不一定相同。 一般一个栅格显示一种颜色,比如1170*2532横向可以显示1170个颜色,如果你手动调节降低屏幕分辨率为600*800,系统就会分配给你600*800个有效像素个数,剩余栅格就由系统自作主张,通过一系列运算给你一个模拟色彩块,填充成正好1170*2532个色彩,这样看上去好像就模糊了。
iphone13pro横向有1170个栅格,是不是表示横向屏幕css宽度100%=1170px呢?当然不是。
这里的css像素和物理像素之间还有一个桥梁 ”设备像素比dpr“,它表示1px css像素对应物理上多少个栅格(物理像素)。这个还拿iphone13pro举例,它的dpr是3,所以在UI方面它的css逻辑像素横向宽度=390dp
这个dp就是和我们前端相关的单位!
一般UI设计师给我们的设计稿,宽度都是375px,比如有这样一个设计稿:
如果 你在iphone13pro上写死左右两个div宽度=187.5px,那么右边会多出15px的空白区域,这个就不是顶格适配了,这个就是移动端由于屏幕宽度不同,需要进行适配的原因。
怎么解决的
有两种方案:rem布局和viewport(vw)布局
rem布局
简单讲就是,每个手机屏幕宽度不一样,那我就拿手机屏幕宽度/设计稿宽度,得出来这个比例,然后把设计稿的每个px都乘上这个倍率。
但是你总不能每写一个样式就乘一下倍率,所以想到rem,在根html直接设置font-size=倍率px,然后其他的样式用rem为单位
let pixelRatio= document.documentElement.clientWidth/375(设计稿宽度);
// css文件
html{
font-size: ${pixelRatio}px
}
div{
padding: 16rem;
}
这样有个问题,假如pixelRatio是个小数比如1.23,那么就是无效的,因为px最小单位是1。所以我们可以做一个调整:
let pixelRatio= document.documentElement.clientWidth/375(设计稿宽度);
// css文件
html{
font-size: ${pixelRatio*100}px
}
div{
padding: (16/100)rem;
}
这样问题就来了,难道我们每次写样式都要做一个除100的计算吗,这样太麻烦了。
这个时候我们就可以借助less的function做一个处理:
$ue-width: 375; /* ue图的宽度 */
@function px2rem($px) {
@return #{$px/$ue-width*100}rem;
}
p {
width: px2rem(187);
}
到此为止其实已经解决了问题,但是其实这个别人也想到了,并且已经做成了npm包,我们只要安装npm并且配置一下就可以了:postcss-px2rem postcss-px2rem-exclude
// postcss.config.js文件
const path = require('path');
module.exports = {
plugins: [
require("autoprefixer")(),
require('postcss-px2rem')({
remUnit: 75
}),
require('cssnano')()
]
};
然后项目中元素的宽度直接按照设计稿的宽度来就直接是一致的啦。
vw布局
vw原理就更简单了,100vw=屏幕宽度,直接省略js获取屏幕宽度的步骤了;比如iphone13pro宽度是460px,下面是rem和vw的对比
// rem原理
html{
font-size: 123px; // (460/375*100)
}
div{
width: 1.87rem; // (187/100)
}
// vw原理
div{
width: 49.87vw; //(187/375*100)
}
但是这么计算也很麻烦,不合适~🙈,所以我们可以继续用别人写好的npm库:postcss-px-to-viewport
具体配置文件,其中的viewportWidth:375是视觉稿的宽度。
'use strict';
module.exports = {
plugins: [
require('autoprefixer')(),
// https://github.com/evrone/postcss-px-to-viewport
require('postcss-px-to-viewport')({
viewportWidth: 375, // 视口宽度(数字)
viewportHeight: 1334, // 视口高度(数字)
unitPrecision: 3, // 设置的保留小数位数(数字)
viewportUnit: 'vw', // 设置要转换的单位(字符串)
fontViewportUnit: 'vw', // 设置字体转换的单位(字符串)
selectorBlackList: ['.ignore', '.hairlines'], // 不需要进行转换的类名(数组)
minPixelValue: 1, // 设置要替换的最小像素值(数字)
mediaQuery: false // 允许在媒体查询中转换px(true/false)
})
]
};