屏幕适配

屏幕适配,一直是作为一个前端开发始终逃不掉的问题,这个话题可以追溯到最开始的PC端浏览器的不同分辨率,再到移动端不同的屏幕尺寸,一直伴随着前端工程师的日常的页面开发工作。所谓屏幕适配,可以理解为一个网页元素或者网页布局,在不同尺寸,分辨率等场景下,如何呈现最佳的效果。 从最早的PC端屏幕来说,大部分的屏幕适配采取的是:

  • 页面框架最外层元素宽度固定,并且居中,高度随内容自适应,比较常见的是宽度为960px~1080px。
  • 页面内部的元素大多数使用盒子模型构建,采用固定宽高,当内容超出时,会出现滚动条。
  • 对于一些需要根据屏幕不同而展示不同大小的元素,可以给元素设置百分比的单位。

随着HTML5和CSS3的到来,逐渐出现了弹性布局(flex布局),媒体查询Media Query,和响应式页面概念,这些特性都可以应用在PC端以及移动端屏幕适配解决方案中。除了这些之外,还有rem和vw方案更加有针对性的解决移动web页面的适配问题。

viewport视窗(视口)

在HTML代码的<head>标签中,都有一行设置的代码,如下:

1
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

这行代码的作用就是设置浏览器的视窗大小,具体的含义我们后面在介绍,在讲解视窗之前,我们首先需要了解一下什么是物理像素和CSS像素。

像素

像素,也就是px,实际是pixel的缩写,它是图像显示的基本单元,每个像素可以有色彩数值和位置,每个图像是由若干个像素组成,比如对一幅标有1024×768像素的图像,就表明这幅图像的长边有1024个像素,宽边有768个像素,共有1024×768=786432个像素组成。

但是从概念上来说,像素既不是一个确定的物理量,也不是一个点或者小方块,而是一个抽象概念。所以像素所代表的具体含义要从其处于的上下文环境来具体分析。物理像素和CSS像素就是不同的上下文。

物理像素和css像素

  • 物理像素:设备屏幕实际拥有的像素点,主要和渲染硬件相关。比如iPhone6的屏幕在宽边有750个像素点,长边有1334个像素点,所以iPhone6总共有750*1334个物理像素。
  • CSS像素:也叫逻辑像素,是软件程序系统中使用的像素,每种程序可以有自己的逻辑像素,在web前端页面就是对应的CSS像素,逻辑像素在最终渲染到屏幕上时由相关系统转换为物理像素。
  • 设备像素比:一个设备的物理像素与逻辑像素之比。可以在JavaScript中使用window.devicePixelRatio获取到。

其实对于早期PC端web页面来说,在的CSS里写个1px,屏幕就给你渲染成1个实际的像素点,此时的设备像素比是1,这时物理像素和CSS像素是一样的。但是对于一些高清屏,例如苹果的retina屏幕,这种屏幕使用2个或者3个物理像素来渲染1个CSS像素,所以这些屏幕的显示效果要清晰很多。例如下图a代表物理像素,b代表CSS像素,它们之间的关系如图下图所示。

图片描述

可以想象一下,一个传统的PC端web页面,如果想要完全放在手机端使用浏览(可以想象成把PC端显示器替换成手机屏幕),一定是放不下的,而这时就需要对页面进行缩放,那么对页面进行放大和缩小,其实就是改变像素比,例如下图,用4个CSS像素和4个物理像素来模拟放大和缩小。

image-20201130105848531

在页面处于正常状态时,4个物理像素的区域需要4个CSS像素刚好展示完,当页面缩小时,原本4个物理像素需要大于4个CSS像素才能显示完这片区域,而当页面放大时,原本4个物理像素需要小于4个CSS像素就可以显示完,或者说是4个CSS像素能够放下更多于4个物理像素的位置。这就实现了页面的放大和缩小,而对于HTML而言,控制放大和缩小的就是视窗Viewport。

物理像素就是上述所说的小点点

编写网页时,我们用到的像素是css像素

浏览器在显示网页时,需要将css像素转换成物理像素,然后再呈现

那么,一个css像素最终由几个物理像素组成,是由浏览器决定的:

默认情况下,一个css像素 = 一个物理像素

CSS像素和物理像素之间的比例取决于屏幕的特性(是否为高密度)以及用户进行的缩放,由浏览器自行换算。

视窗

在了解了物理像素和CSS像素的概念之后,然后就需要引入下一个概念,移动设备中的视窗,视窗就是浏览器显示页面内容的屏幕区域,有3种不同的类别,主要分为:

  • 物理视窗(Visual Viewport):表示物理屏幕的可视区域,屏幕显示器的物理像素,也就是长宽边上有多少个像素点。同样尺寸的屏幕,像素点越多,像素密度越大,它的硬件像素会更多。可以理解成物理视窗的大小就是屏幕的大小。

  • 布局视窗(Layout Viewport):是由浏览器厂商提出的一种虚拟的布局视窗,用来解决页面在手机上显示的问题。这种视窗可以通过<meta>标签设置viewport来修改。每个浏览器默认都会有一个设置,例如iOS,Android这些机型设置布局视窗宽度为980px,所以PC上的网页基本能在手机上呈现,只不过元素看上去很小,一般可以通过手指动双击缩放网页。

  • 理想视窗(Ideal Viewport):理想中的视口。这个概念最早由苹果提出,其他浏览器厂商陆续跟进,目的是解决在布局视窗下页面元素过小的问题,显示在理想视口中的页面具有最理想的宽度,用户无需进行缩放。所以理想视窗就相当于把布局视窗修改成一个理想的大小,这个大小和物理视窗基本相等。

如下图,可以表示物理视窗和布局视窗的关系,底部的网页大小相当于布局视窗,而半透明灰色区域表示物理视窗大小,看起来就像一个手机屏幕大小。

image-20201130110837467

所以如果想要在物理视窗里面完全展示布局视窗里的内容,肯定要将页面缩小。那么缩小到多少合适呢,就需要有理想视窗,如下图所示。

image-20201130111021970

设置Viewport

对于移动端web页面,可以采用<meta>标签对视窗的大小,缩放等进行配置,也就是之前提到的在<head>标签内设置的<meta>的代码如下:

1
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

其中,可以配置的属性含义如下:

  • width:该属性被用来控制视窗的宽度,可以将width设置为320这样确切的像素数,也可以设为device-width这样的关键字,表示设备的实际宽度,一般为了自适应布局,普遍的做法是将width设置为device-width。
  • height:该属性被用来控制视窗的高度,可以将height设置为640这样确切的像素数,也可以设为device-height这样的关键字,表示设备的实际高度,一般不会设置视窗的高度,这样内容超出的话采用滚动方式浏览。
  • initial-scale:该属性用于指定页面的初始缩放比例,可以配置0.0~10的数字,initial-scale=1表示不进行缩放,视窗刚好等于理想视窗,当大于1时表示将视窗进行放大,小于1时表示缩小。这里只表示初始视窗缩放值,用户也可以自己进行缩放,例如双指拖动手势缩放或者双击手势放大。
  • maximum-scale:该属性表示用户能够手动放大的最大比例,可以配置0.0~10的数字。
  • minimum-scale:该属性类似maximum-scale,用来指定页面缩小的最小比例。通常情况下,不会定义该属性的值,页面太小将难以浏览。
  • user-scalable:该属性表示是否允许用户手动进行缩放,可配置no或者yes。当配置成no时,用户将不能通过手势操作的方式对页面进行缩放。

在使用<meta>标签设置viewport时有几点需要注意,首先viewport只对移动端浏览器有效,对PC端浏览器是无效的,其次对于移动端浏览器,某些属性也并不是完全支持,例如对于iOS的Safari浏览器,从10.0版本开始将不在支持user-scalable=no,所以即使设置了user-scalable=no,用户依然可以对页面进行手势操作来缩放。如果依然需要禁用,可以参考如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
window.onload = function () {
document.addEventListener('touchstart', function(event) {
// 当两个手指操作
if (event.touches.length > 1) {
// 组织浏览器默认事件
event.preventDefault();
}
});

var lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
// 判断是否是双击操作,即两次点击间隔小于300ms
if (now - lastTouchEnd <= 300) {
// 组织浏览器默认事件
event.preventDefault();
}
lastTouchEnd = now;
}, false);
}

通过手势来进行缩放是属于浏览器的默认功能,上面代码的原理就是利用event.preventDefault()方法,来禁用浏览器的默认事件,这样就不能触发这个默认的缩放功能。具体逻辑可以将代码运行之后看一下效果。 Viewport视窗的相关知识点是了解移动web适配的基础,通过动态的设置viewport可以实现不同屏幕下的页面适配,例如对设备像素比不为1的机型进行缩放,强制让物理像素和CSS像素相等,代码如下:

1
2
3
4
5
6
7
(function(){
var scale = 1/window.devicePixelRatio;
var meta = document.createElement("meta");
meta.name = "viewport";
meta.content = "width=device-width,initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale;
document.head.appendChild(meta);
})();

这种方法有时候不准确,比如devicePixelRatio不为整数的时候,会出现除不尽的情况,那缩放的倍数就会出现很长的小数,再去算物理像素的时候就会有误差,所以现在大部分移动web页面采用更加完善的rem或者vw加flex的方案来进行适配。

Rem适配

Rem适配方案是当下流行并且兼容性最好的移动端适配解决方案,它支持大部分的移动端系统和机型,Rem实际上是一个字体单位,即rem(font size of the root element)是指相对于根元素的字体大小的单位,简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。 所以Rem适配方案的适配原理就是:将我们之前写px的单位换成rem单位,然后根据屏幕大小动态设置根元素<html>的font-size大小,那么只要跟元素的font-size改变,对应的元素的大小就会改变,从而达到在不同屏幕下的适配的目的。

动态设置根元素font-size

使用浏览器浏览网页时,网页中的字体大小由根元素<html>来决定,而<html>的字体大小由浏览器本身决定,在不修改浏览器默认字体情况下是16px,即默认情况下1rem = 16px,但是如果采用Rem的适配方案就需要动态设置<html>的font-size。一般情况下是根据屏幕的宽度来动态设置,即采用屏幕宽度来识别不同的机型,以达到对不同机型的适配,具体有两种方案来设置,第一种是采用媒体查询(Media Query),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@media screen and (min-width:461px){
html{
font-size:18px;
}
}
@media screen and (max-width:460px) and (min-width:401px){
html{
font-size:22px;
}
}

@media screen and (max-width:400px){
html{
font-size:30px;
}
}

上面代码中,使用screen媒体特性,来定义了3组屏幕宽度区间,当小于400px,大于401px且小于460px,大于461px,当屏幕宽度位于不同的区间时,则会应用上对应的<html>的font-size。 另外一种则是使用Javascript动态设置<html>的font-size,代码如下:

1
2
3
4
5
// 获取屏幕视窗宽度
let htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;// 获取宽度最好有个兼容的方案,避免某些情况下第一种获取不到可以选择第二种
//获取html
let htmlDom = document.getElementsByTagName('html')[0];
htmlDom.style.fontSize = htmlWidth / 10 + 'px'; //求出font-size

上面代码中,得到屏幕宽度后,一般要除以一个系数,这里使用的系数是10,这样得到的font-size值更加灵活,适配性更强,所以实际应用当中,大多数采用的JavaScript来动态设置。如果想要实时监听屏幕大小的变化动态修改font-size,可以引入resize事件,代码如下:

1
2
3
window.addEventListener('resize',function(){
/*上面设置font-size的代码*/
})

计算rem数值

设置完font-size之后,就可以直接利用rem单位来给我们的div或者其他元素设置宽高等等的属性了,这里就有一个问题,我们一般拿到的UI稿都会提供标注,这些标注一般会标识出某个元素例如按钮,图片具体大小数值,单位是px,并且整个UI稿都会基于一个具体的移动设备,例如iPhone6s等,可以参考下图所示。

image-20221130140433244

那么,我们如何根据视觉稿上的px单位值转换成对应的rem单位值呢?这里举一个例子,一个按钮在视觉搞上标注的大小是:宽200px,高400px,那么我们根据这个来进行如下计算:

以iPhone6s视觉搞来说,屏幕是375*667单位是px。

根据上面JavaScript方法设置的<html>的font-size得到是37.5px,这里37.5px称做rem的基准值,下面的计算会用。

根据1rem=37.5px,得到200px=5.3rem,400px=10.6rem。

根据上面的方法,我们就可以给按钮元素设置rem单位了,代码如下:

1
2
3
4
5
6
.button {
width: 5.3rem;
height: 10.6rem;
font-size:0.53rem;
background-color: red;
}

我们给一个元素采用了rem单位来设置了宽高,那么这个元素在不同机型中显示时,由于设置的根元素<html>的font-size大小不一样,那么rem所实际渲染出来的大小也就不一样,可以比较一下分别在Chrome开发者工具中的Device Mode中采用iPhone6s和iPhone6P运行的效果区别,如图下图所示。

image-20221130140632109

如上图所示,对于同一个按钮,在不同的机型上表现出的大小是不一样的,这就是rem带来的适配效果。 当然,采用rem适配,必须针对rem基准值来将px转换成对应的rem值,这个计算是很繁琐的一件事情,但是这个工作可以交给Sass[ Sass(英文全称:Syntactically Awesome Stylesheets)是一个最初由Hampton Catlin设计并由Natalie Weizenbaum开发的一个CSS预处理器,采用类CSS语法并在最后解析成CSS的脚本语言。]来帮助我们完成,例如可以在Sass代码中定义一个公式,代码如下:

1
2
3
4
5
6
7
8
9
10
@function px2rem($px){
$rem: 37.5;
@return ($px/$rem) + rem;// $px表示变量,+号表示拼接,rem为字符串相当于'rem'
}
.button {
width: px2rem(200);
height: px2rem(400);
font-size: px2rem(20);
background-color: red;
}

当然,上面的代码已经不是一个标准的CSS代码了,而是一个Sass语言的CSS代码,不过没有学过Sass也没有关系,我们只会用到Sass的很少一部分知识点。 上面代码中,定义了一个方法,方法名为px2rem,这个方法接收一个参数就是将要转换的px值,然后根据rem基准值来计算。当在给元素设置宽高时,调用这个方法即px2rem(200),将需要转换的px值作为参数传递进去,这样经过编译后,最终得到的就是rem单位的值了即width: px2rem(200)转换成了width:5.3rem。 总结下来,使用Rem适配方案主要有以下几点需要注意:

  • 首先需要有一段JavaScript脚本来动态设置根元素<html>的font-size,这段脚本一般放置在<head>标签里面,让font-size更早的设置,可以让适配更早的生效。

  • 一旦页面使用了Rem适配,那么除特殊情况除外(例如雪碧图定位background-position时),页面中凡是用到px为单位的元素都应该改为rem单位,这样才能做到整体适配。

  • 对于宽度比高度大很多的机型例如横屏下的iPad以及一些手写笔记本,是不适合采用Rem方案的,因为宽度较大会导致<html>的font-size设置不准确。另外就是一些小说网站,屏幕越小的移动设备如果用了rem单位就会导致文字就越小,就会导致看文章的时候特别费眼。

vw适配

vw其实也是一个CSS单位,类似的还有vh,vmin,vmax共四个单位,这些单位伴随着CSS3的出现就已经有了,但是当时移动web的浪潮已经来临,并且Rem出现的要早一些,所以很多开发人员对此并不熟悉。 和Rem适配方案相比,vw适配方案不需要使用JavaScript脚本来提前设置font-size,vw适配方案完全基于CSS自身,这也是相对于Rem适配方案的优势所在,并且对于横竖屏切换较为频繁的页面时可以采用vmin单位,更加灵活。我们先来了解一下vw,vh,vmin,vmax这几个单位,含义如下:

  • vw : 1vw 等于视口宽度的1%。

  • vh : 1vh 等于视口高度的1%。

  • vmin : 选取vw和vh中最小的那个,1vmin等于视口宽度的1%和视口高度的1%中最小的值。

  • vmax : 选取vw和vh中最大的那个,1vmax等于视口宽度的1%和视口高度的1%中最大的值。

从上面的解释可以看出,vw和vh这些单位也并不是一个固定的值,而是根据视口宽度或者高度而变。那么什么是视口呢?还记得之前讲解的viewport吗,通过标签:

1
<meta name="viewport" content="width=device-width">

设置的这个宽度就是视口宽度,并且可以通过JavaScript中的document.documentElement.clientWidth或者document.body.clientWidth获取到这个值,这里就和前面讲解Rem适配方案时获取屏幕宽度时的用法是一样的。 有些同学会遇到例如window.innerWidth或者window.screen.width来获取屏幕或者视口的宽度,这种方法获取到的一般是设备的物理宽度,例如真实的分辨率或者物理像素值,这个和视口宽度不一定相等,当<meta>标签设置viewport时,如果width=!device-width时,这种情况下就是不相等的,所以各位在使用时还是需要注意一下。

计算vw数值

对于vw适配方案,也是需要计算vw值的,同理我们还是以iPhone6的UI稿为例子,例如一个按钮在视觉搞上标注的大小是宽200px,高400px,那么我们根据这个来做如下计算:

  • 以iPhone6s视觉搞来说,屏幕是375*667单位是px。
  • 根据1vw等于视口宽度的1%,即1vw等于3.75px,得到200px=53vw,400px=106vw(这里取整)。

根据上面的方法,我们就可以给按钮元素设置vw单位了,代码如下:

1
2
3
4
5
.button {
width: 53vw;
height: 106vw;
background-color: red;
}

和计算rem值同理,也可以利用Sass来声明一个方法,做px到vw的转换,代码如下:

1
2
3
4
5
6
7
8
9
@function px2vw($px) {
$vw: 3.75;
@return ($px/$vw)+vw;// $px表示变量,+号表示拼接,vw为字符串相当于'vw'
}
.button {
width: px2vw(53);
height: px2vw(106);
background-color: red;
}

Rem适配和vw适配兼容性

从上面的对两种相关的适配方案讲解,可以知道vw适配方案要优于Rem适配方案的,但是没有Rem流行就在于vw的兼容性问题,我们从caniuse[ caniuse是一个当下流行的前端技术兼容性查询网站,地址是:www.caniuse.com/]网站中查询到兼容性如下。

image-20221201062422170

Rem适配方案在主流浏览器中整体支持性98.93%,而vw适配方案在主流浏览器中整体支持性94.44%,并且对于Android4.4之前的机型来说vw不支持是硬伤,毕竟这部分机型的市场占有率还是有一部分的。所以各位在选取适配方案时,要根据自己业务的场景来选择合适的方案,避免出现兼容性问题。

视口就是屏幕中,显示网页的区域;简单说,视口就是浏览器的窗口

视口是可以拖拽,即大小是可以改变的

可以通过查看视口的大小,来观察css像素和物理像素的比值

1
打开浏览器,查看html元素的宽度,即为视口的长度(一般只看宽度)

默认情况下,审查元素查看html元素宽度:

1
2
视口宽度:1920px(css像素)
1920px(物理像素)

放大网页时,可视区域变小,视口变小

放大两倍的情况,审查元素查看html元素宽度:

1
2
3
视口宽度:960px(css像素)
1920px(物理像素)
此时,css像素:物理像素 = 1:2

在放大两倍的情况下,创建一个100px的正方形盒子,用截图工具量取物理像素时,是200px

要理解的是,css像素和物理像素不一样;我们可以通过改变视口的大小,来改变css像素和物理像素的比值

手机像素

在不同的屏幕,单位像素的大小是不一样的,像素越小屏幕越清晰

1
2
以电脑为例,24寸 1920 x 1080 px,可以算出一寸有多少像素
iphone6,4.7寸 750 x 1334 px

这里关注移动端,查看不同手机机型的分辨率

智能手机的像素点,远远小于计算机的像素点

问题:一个宽度为900px的网页,在iphone6中要如何显示呢?

1
2
3
4
5
6
7
8
9
10
创建宽为900px的盒子,审查元素调为手机端,查看iphone6机型的显示模式,发现还没有把宽度撑满

上述的750px是物理像素,900px是css像素,两者不能直接比较,应该看视口大小

查看iphone6的视口大小,为980px(一般移动端的视口大小都是980px),以确保没有做手机适配的网页,在手机端可以完整展示(以前的电脑端网页宽度一般不会超过1000px)
当把创建的盒子的长度,设置为980px时,移动端刚好撑满

但是如果网页端的宽度超过了980px,移动端的浏览器会自动缩放,以保证一次性可以把网页看全,比如腾讯新闻网:https://news.qq.com/

所以基本上大部分的pc端网页都可以在移动端正常访问,但往往不会有好的体验,为了解决这个问题,大部分网站都会专门为移动端设计网页

image-20220113210322548

完美视口

移动端默认的(布局)视口大小是980px(css像素)

对于iphone6而言,移动端的像素比就是css像素/物理像素

980/750 = 1/0.77,表示一个css像素,占0.77个物理像素

如果直接在网页中编写移动端代码,这样在980视口下,像素比非常不好,导致网页中的内容非常小

所以,编写移动端页面时,必须确保有一个比较合理的像素比

1
2
1 css像素 对应 2 物理像素
1 css像素 对应 3 物理像素

那么,怎么调整像素比呢?

1
可以通过改变视口的大小,来改变css像素和物理像素的比值

那么,怎么调整视口大小呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
可以通过meta标签,来设置视口大小

name:属性名
content:属性值
//设置视口的大小为100px
<meta name = "viewport“ content="width=100px">

此时再设置盒子宽为100px,手机端正好可以把屏幕撑满

此时像素比是100/750,此时一个css像素就占7.5个物理像素,而此时移动设备的分辨率是750像素,所以只要设置100px的css像素,即可撑满手机屏幕
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=100px"/>
<title></title>
<style>
*{
margin: 0;
padding: 0;
}
.box1{
width: 100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<div class="box1"></div>
</body>
</html>

完美视口

那么,css像素究竟写多少,像素比究竟是多少才最合适呢

每一款移动设备设计时,都会有一个最佳的像素比

一般我们只需将像素比设置为该值即可得到一个最佳效果,以iphone6为例,查看不同手机机型的分辨率发现iphone6的最佳像素比为2,算出视口大小为375px,这样的视口刚好和iphone6实际的硬件宽度一致。

所以我们将像素比设置为最佳像素比的视口大小称为完美视口

那么,375px是iphone6的完美视口,会是其他机型的完美视口吗?

答案是否定的。

所以设置content = device-widthdevice-width是浏览器提供的变量,表示设备的宽度,即完美视口

设置完美视口

1
2
3
<meta name="viewport" content="width=device-width, initial-scale=1.0">
所谓的完美视口,就是对于不同的移动设备来说,使其布局视口依赖于自己的设备宽度,这样我们如果想要撑满某个机型的宽度,只需要设置刚好等于其宽度的css值即可(虽然可以通过width:100%来设置)

结论

以后再写移动端的页面,就把上面的代码写上。

vw单位

不同的设备,完美视口的大小是不一样的(硬件本身的设备宽度就不一样,导致了在css渲染时依赖的布局视口宽度不一样)

1
2
iphone6 -- 375
iphon6plus -- 414

由于不同设备视口和像素比不同,所以同样的375像素在不同的设备下,意义是不一样的

1
2
比如在iphone6,375就是全屏,
而到了iphon6plus中375就会缺一块

完美视口的设置,只解决布局视口的依赖问题,但没有解决同一个css像素,在像素比下的呈现问题。

所以在移动端开发中,就不能再使用px来进行布局了

vw表示的是视口宽度(viewport width)

  • 100vw = 一个视口宽度
  • 1vw = 1/100 = 1% 视口宽度

vw这个单位永远相当于视口宽度进行计算

1
在学习js之前,移动端可以用vw来进行适配

px与vw单位的比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!--
* @Author: your name
* @Date: 2022-01-13 20:46:18
* @LastEditTime: 2022-01-15 10:21:33
* @LastEditors: Please set LastEditors
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @FilePath: \布局\视口.html
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title>Document</title>
<style>
body {
margin: 0;
padding: 0;

}

.outer .inner {

width: 50vw;
height: 200px;
background-color: orange;
}
.outer .inner2 {
width: calc(375px / 2);
height: 200px;
background-color: blueviolet;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner">1</div>
<div class="inner2"></div>
</div>
<script>
var cWidth = document.documentElement.clientWidth
console.log(cWidth)
</script>
</body>
</html>

iphone6机型

image-20220115102250300

iphone6 plus机型

image-20220115102316095

可以看到,在以vw为单位时,不同机型的呈现效果是同步的,因为是基于视口宽度动态计算的,而375px的盒子宽度,在iphone6上(布局视口750)是占据了一半,但在iphone plus(布局视口414)上并没有占据一般,所以对于移动端,单位不能用px写死,其本质是即使动态设置了布局视口,但不同机型的分辨率不一样,导致渲染的最终结果就不一样,不如让单位也动态计算。

设计图的宽度

现在一般设计图的宽度,都是按照苹果的375px的布局视口来做的

现在的设计图都是375的倍数

1
2倍图:750px3倍图:1125px
设计图宽度 单位
750px vw

现在想创建一个48 x 35 大小的元素(设计图里的大小)

1
2
3
100vw = 750px  ---->   1px = 100/750vw = 0.13333vw
?vw = 48px ----> 100*48/750 = 6.4 vw
同理35px = 4.667vw

em和rem

1em = 一个字体大小

1rem = 一个html的字体大小

em单位为一个相对的度量单位,它通过寻找父标签的font-size。然后通过计算得出自身的font-size。利用em单位设置便签的width或者height等属性原理也一样。

rem是一个灵活的、可扩展的单位,由浏览器转化像素并显示。与em单位不同,rem单位无论嵌套层级如何,都只相对于浏览器的根元素(HTML元素)的font-size。默认情况下,html元素的font-size为16px。

可以借助rem来存储上述的比值;

1
2
3
4
5
6
7
8
9
10
11
12
13
html{        
<!--font-size:100px;-->
font-size:0.1333333vw;
//0.1333333vw = 1px
}

.box{
width:750rem;

// 750rem = 750 * 1个html字体大小 = 750 * 0.13333vw = 750 * 1px = 750px
height:4.667vw;
background-color:orange;
}

那么,现在想创建一个48 x 35 大小的元素(设计图里的大小)

可以写成下述形式吗

1
2
3
4
5
6
7
8
html{
font-size:0.1333333vw;
}
.box{
width:48rem;// 6.24px
height:35rem;// 4.55px
background-color:orange;
}

不能,因为网页字体大小,最小是12px,不能设置一个比12px还小的值(除了0),否则,字体自动设置为12px;

1
2
3
所以,当设置html{font-size:0.1333333vw}时,默认的值大小其实是12px;,所以实际展示的是盒子大小是 576 x 420px

可以在指定font-size时,扩大倍数,在指定宽高时,再除以对应的倍数;

视口理论

viewport

Web开发:布局视口、视觉视口、理想视口

拓展:https://juejin.cn/post/6844904007756939271

拓展:浅谈移动端中的视口(viewport) - 掘金 (juejin.cn)

rem适配

实现rem适配要做两件事

  • 把px设置成rem
    • postcss-pxtorem
  • 根据设备宽度动态设置根节点的fontsize
    • 手写
    • amfe-flexible

方案一

将px设置成rem

需要借助postcss-px2rem-exclude插件:postcss-px2rem-exclude - npm (npmjs.com)

安装:npm i postcss-px2rem-exclude(使用时有问题,废弃)

配置:根目录新建.postcssrc.js

1
2
3
4
5
6
7
8
module.exports = {
'plugins': {
'postcss-px2rem-exclude': {
remUnit: 80,
exclude: /node_modules|folder_name/i
}
}
}

使用:

1
2
3
header {
height: 100px;
}

结果:

image-20221201101601683

问题postcss-px2rem-exclude库会导致一些问题

1
2
3
npm install postcss-pxtorem --save-dev

npm i autoprefixer

更新

使用postcss-pxtorem这个库,对应的需要安装下autoprefixer

根目录新建.postcssrc.js,也可以是postcss.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
// https://www.npmjs.com/package/postcss-pxtorem

const autoprefixer = require('autoprefixer');
const px2rem = require('postcss-pxtorem');

module.exports = {
plugins: [autoprefixer(), px2rem({
rootValue: 80, // 换算你的基数,如果是pc端和html-font-size保持一致即可,记得同步vscode插件设置
unitPrecision: 5, // 小数点位数
propList: ['*'],
exclude: /node_modules/
})],
};

注意

自定义动态设置根节点字体

新建src/utils/flexible.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(function flexible(window, document) {
var docEl = document.documentElement;
var dpr = window.devicePixelRatio || 1;

// adjust body font size
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = 12 * dpr + "px";
} else {
document.addEventListener("DOMContentLoaded", setBodyFontSize);
}
}
setBodyFontSize();

// set 1rem = viewWidth / 10
function setRemUnit() {
var rem = docEl.clientWidth / 24;
docEl.style.fontSize = rem + "px";
}

setRemUnit();

// reset rem unit on page resize
window.addEventListener("resize", setRemUnit);
window.addEventListener("pageshow", function(e) {
if (e.persisted) {
setRemUnit();
}
});

// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement("body");
var testElement = document.createElement("div");
testElement.style.border = ".5px solid transparent";
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if (testElement.offsetHeight === 1) {
docEl.classList.add("hairlines");
}
docEl.removeChild(fakeBody);
}
})(window, document);

注意点

vue-cli4中配置

备注:这种方式还没用过,目前用的方案一

vue-cli4中配置移动端自适应postcss-pxtorem

1
2
3
npm install postcss-pxtorem --save-dev

npm i amfe-flexible

amfe-flexible来根据屏幕动态改变根元素font-size,postcss-pxtorem把代码中px转为rem;在项目根目录建vue.config.js

vue.config.js内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
css: {
loaderOptions: {
postcss: {
plugins: [
require("autoprefixer")({
// 配置使用 autoprefixer
overrideBrowserslist: ["last 15 versions"]
}),
require("postcss-pxtorem")({
rootValue: 75, // 换算的基数
// 忽略转换正则匹配项。插件会转化所有的样式的px。比如引入了三方UI,也会被转化。目前我使用 selectorBlackList字段,来过滤
//如果个别地方不想转化px。可以简单的使用大写的 PX 或 Px 。
selectorBlackList: ["ig"],
propList: ["*"],
exclude: /node_modules/
})
]
}
}
}
};

然后在main.js里引入amfe-flexible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import 'amfe-flexible'


Vue.config.productionTip = false

new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

注:如果你创建时候选择了prettier格式化代码而且编译器也装了插件,你的css里不想被转化的”PX”编译器会自动帮你转化为”px”,此时只要在不想转化前一行加上

/* prettier-ignore */即可

vue-cli5中配置

见方案一

vite中配置