开始之前先粗略讲一下各种概念,如果要深入理解可以谷歌关键词到处逛逛。如果对这些概念了如指掌,那就可以直接拉到第二块二、实现自适应布局区域。
一、各类名词概念
1、各类像素
(1)物理像素(dp —— device pixels)
概念
说人话就是屏幕上每一颗最小发光点就是1个物理像素。
ppi
(每英寸像素量)是屏幕像素密度的单位,像素密度越大,屏幕显示细节就能更多,画质更加细腻,文本更加清晰。。。但还是看不清楚打马赛克的图片。我们一直谈到的分辨率就是 横向像素点的数量X竖向像素点的数量 px,比如,1920 X 1080 px。
单位
- 单位 —— dp(在不用区分的情况下会表示为 px )
(2)独立设备像素(dips —— Device Independent Pixels )
需求
我们由上文知道,屏幕在相同量的物理像素下,不同的
ppi
,物理宽度不同,如果按照dp来布局,在dp相同都情况下,比如说要展示的模块宽度都为500dp,一只手机的宽度为1000dp,另一只手机的宽度为500dp,并且两只手机的物理宽度相同(举个栗子,都为5cm),那这个模块在后一个手机上能显示5cm,在前一个手机上却只能显示2.5cm。有时候我们不管是做原生app还是移动端web网页,想要一个模块在不同设备上实际显示物理大小差不多,这时候就要使用到独立设备像素了。
概念
字面意思,独立于设备物理像素的像素点概念,在不同设备上,相同数量的一排独立设备像素的物理宽度近乎相同,为什么说是近乎相同呢,因为难免各种屏幕的制作工艺之间的误差,如果想要再深入,具体可以看 这篇 对独立设备像素的描述。我们在web和app上都会用到这个像素单位,来保持视觉宽度效果的一致性。
不足
(再盗一张图。。。)如下图所示,在一些ppi高的手机中(比如二倍屏),原本一块区域可以由4x4个像素点构成,现在使用了独立设备像素作为单位后就只有2x2个像素点构成,对于一些颜色变化不多的布局样式还行,但对于图片,会大大损失它但细腻度,清晰度,也就是这时候用屏幕好的手机和屏幕差的手机看网页上的图片画面一样差,那岂不是亏了钱去买屏幕好的手机了。。。
单位
- 单位 —— px(各种客户端/web)
(3)css像素(属于独立设备像素)
概念
在默认情况下,css像素可以看作独立设备像素,它与物理像素的比值相同于独立设备像素与物理像素的比值,但是暂时只在浏览器上应用css像素这个概念。在web端,我们可以使用
meta
标签中initial-scale
来进行缩放以改变它与物理像素的比值。
单位
- 单位 —— px(web)
2、viewport(视口)
视口分为3种,我们先来看前面两种,ppk将他们分别称为
(1)布局视口(layout viewport)和虚拟视口(visual viewport)
盗两张他的图
布局视口
第一张是布局视口的图,布局视口就相当于是网页的父容器,出现布局视口是有历史原因的,在很久以前,并没有网站单独为移动端做适配,所以如果没有布局视口的概念,直接将一个pc端的网页拿到移动端,就会出现下面这种情况(当然为了还原当时的手机分辨率,这张图改过像素大小,现在的手机还是能依稀看到网站上的内容的)。
这种网站在手机上咋用对吧,就算能看清,点摁都是问题。所以就有了布局视口,也就是图一的样式,用户可以拖拉以看清内容和能准确点击到相应的元素。
在大多数默认情况下,它的css像素宽度为980px,在不同浏览器可能表现不一样。可以通过document.documentElement.clientWidth
来进行获取。
可以通过meta
标签的width
进行设置。
<meta name="viewport" content="width = 1000">
单位为css像素的px。
虚拟视口
如图一右边
虚拟视口就是浏览器可视区域,在默认情况下,它所占的css像素大小的值等于浏览器可视区域所占独立设备像素的大小的值;所占物理像素大小永远等于浏览器可视区域的大小。
我们可以通过window.innerWidth/innerHeight
来获取到它的长和宽,单位是css像素的单位px。
而随着移动端热度不断上涨,为了提高移动端用户的体验,需要有一些移动端专有的网页设计,向原生app靠近。于是就有了
ideal viewport
也就是自适应视口
(2)理想视口
概念
每类手机都有一种理想视口,理想视口的意思是,不同手机可能有不同的默认虚拟视口能够看到的css像素量的值,而这个值要为不同手机做一个适配,让里面的布局能够在不同设备中都能清晰地呈现,不会太大,也不会太小)
实现
我们依旧可以通过这个meta
标签实现自理想视口。
<meta name="viewport" content="initial-scale=1.0" />
这时候可能会有疑惑,其他博客都加了width=device-width
为什么这边不加。
这得从initial-scale
和width
这两个的赋值作用说起
width
width=device-width
指视口宽度的值等于屏幕基于独立设备像素的宽度值。
在安卓等众手机中表现良好,但在苹果众产品中就有个缺陷——横屏时候屏幕独立设备像素宽度取的还是竖屏时候的值。
initial-scale
initial-scale = 1
是能取消默认布局视口大小,如果页面本身布局视口大小大于虚拟视口,则布局视口按原本的来,如果页面本身布局小于虚拟视口,则其与虚拟视口大小一样。
但是它在 windows phone 的ie上也出现了横屏时候屏幕独立宽度取的还是竖屏时候的值这个bug。
所以当初为什么两个混用呢,因为当其中一个值出错的时候,会使用相比大的那个值,达到兼容的效果。
现在嘛。windows phone不是凉了嘛。width=device-width
就没必要写了。
3、rem
概念
1rem等于根元素(html)
font-size
样式属性的值
使用
如果我们全局使用
rem
这个单位,更改html元素上font-size
的值就可以改变全局所有使用了rem元素的css像素大小。
比如我们设置成
<html style="font-size:20px"></html>
那接下来所有的1rem就等于20px了。
二、实现自适应布局
对于自适应布局,我们一共要实现两个需求——
1、增加图片在二倍屏、三倍屏等屏幕像素密度高的手机中的清晰度。
2、解决因为各色各样的手机屏幕宽高差异,而导致适配问题以致设计师以及前端工程师的额外工作。
虚拟视口中的css像素默认情况下物理大小与设备独立像素大小一致,而设备独立像素这个概念就是为了在不同设备中显示效果一致而生的,所以默认的虚拟视口的大小完全可以作为理想视口的大小。普通的自适应就可以以手机默认虚拟视口的大小来设计填充屏幕,然后拥有多倍屏的手机就只要经历一下倍屏的逻辑处理让img显示更精细。
需求一
增加图片在二倍屏、三倍屏等屏幕像素密度高的手机中的清晰度。
因为css像素默认情况下等于独立设备像素,在高倍屏手机下,它并不能较好得利用到每一个屏幕物理像素点(前文对独立设备像素对讲解中有提到)
如何进行充分利用呢——当然是将其与物理像素做一一对应,利用到每一个物理像素点。
所以要解决的事情就出来了,我们需要——
使css像素缩放到和物理像素相同大小
怎么缩放呢?
其实前边已经给了思路
initial-scale
能对浏览器内容的整体缩放来改变css像素与物理像素的比值。
在js中,我们可以通过window.devicePixelRatio
来获取到css像素与物理像素的比值。
设比值 dpr = b
也就是说这时候css像素比物理像素的物理大小大b
倍。在i x i
的物理像素中,只能存放(i/b)x(i/b)
个css像素。
在<meta name="viewport" content="initial-scale=1/b" />
中我们可以将css像素缩小到1/b
,但不改变元素css像素数值。
这时候css像素就和物理像素一样大了,并且在相同大小的物理空间中css像素可以达到和物理像素一样的数量。
所以用js操作就是
使用计算出应该缩放的值为 1/window.devicePixelRatio
并且重新创建meta
元素写入页面
//生成类似的元素写入页面
<meta name="viewport" content="initial-scale=b" />
第一个很简单,现在开始解决第二个问题
解决屏幕宽高差异带来的额外工作
为了节省工作量,在我们公司,使用的是统一为375px
(下面用变量c
来表示)宽度为标准的切图稿。
现在想要将这份稿件适配到各种规格的屏幕上,该如何适配呢。
因为initial-scale=1/b
的功劳,我们也同时实现了自适应视口,也就是说,现在的布局视口大小与虚拟视css像素大小相同,因为虚拟视口大小的获取window.innerWidth
有兼容问题,所以我们使用document.documentElement.clientWidth
来获取到视口宽度为d
px。
首先,我们需要先将稿件宽度值等于设备浏览器可视区域所占css像素
的值。所以,需要将稿件内所有元素
的css像素宽高度乘以视口大小与切图稿的比值,也就是d/c
。
现在问题又来了,那么多需要动态设置的元素长和宽,如何才能一下子设置完呢。
这就要用上rem
继承根元素字体大小的特性了。
在我们模仿了切图稿上的刻度做完网页后。
要使用rem
控制全部变量的话,我们需要使用webpack做一个预处理(可以用postcss),将所有元素的宽和高从 px
直接修改变成 rem
。
并且设置 html
元素的 font-size
初始值为 e
px。
这时候元素的宽度为原本的 e
倍肯定是不对的,所以我们依旧要使用webpack将每个元素的值缩小e
倍。
这时候每个元素的宽就和原来一样了。
现在我们开始通过控制根元素的font-size
值来控制全局元素的css像素宽度,我们需要将页面内所有元素
的css像素宽高度乘以视口大小与切图稿的比值,也就是d/c
。
就是将font-size
的值设置成e x d / c
px。
这样就实现了第二个需求
总结一下
总结一下我们能获取的各种变量。
const b = window.devicePixelRatio//dpr
const c = '某个自己设置的值,我们公司为375px' //切图稿的统一宽度
const d = document.documentElement.clientWidth//视口宽度
const e = '某个自己设置的值,我们公司为37.5px' //根元素font-size大小
- webpack做了两件事,一件是给
html
元素设置初始的值e
px,另一件是将所有元素的css样式中的px
改为rem
,并且值缩小e
倍。 - js也做了两件事,一件创建
meta
标签是动态缩放1/b
来设置页面的自适应视口
,另一件是重新设置根元素的font-size
的值为e x d / c
px。
有人可能有疑问,为什么要加const e = '某个自己设置的值,我们公司为37.5px' //根元素font-size大小
这个东西呢,这个东西设置为1px省好多步骤不是吗
我们来看看,这时候1rem是不是等于 375px/37.5px = 10
也就是会一直等于页面大小的1/10,可以利用它来达到百分比的布局。
说到百分比的布局,就会想到vw和vh,为什么不用vw和vh呢,因为当时的浏览器支持问题,之后肯定会引入vwvh布局。
注意事项
webpack并不能设置js中的操作dom元素的值。
所以在js中需要我们手动来完成。
第一种方法是将px
转化成rem
再除以e
第二种方法非要用px作单位的话就把值除以c
再乘以d