文章目录
- 相关概念
- (一)屏幕相关
- 1. 屏幕大小
- 2. 屏幕分辨率
- 3. 屏幕密度
- (二)像素相关
- 1.物理像素
- 2. css 像素
- 3. 设备独立像素
- 4.像素比
- 5.像素之间的关系
- (三)图片高清显示
- 位图像素
- 图片的高清显示(媒体查询)
- (四) 视口相关
- PC端视口
- 移动端视口
- 1. 布局视口 Layout viewport
- 示例
- 2.视觉视口 Visual viewport
- 3.理想视口 Visual viewport
- 理想视口的特点
- 示例
- 【总结】
- (五)缩放
- PC端
- 移动端
- viewport
- viewport相关选项
- 1. width
- 示例
- 2. initial-scale
- 3. maximum-scale
- 4. minimum-scale
- 5. user-scalable
- 6. viewport-fit
- 写法
- 适配
- 为什么要做适配?
- 问题演示
- 1.viewport适配
- 示例
- 2.rem适配
- em和rem介绍
- 示例
- rem方案一:核心原理
- 推算过程
- 示例
- rem方案二:核心原理
- 示例
- rem方案二 + less版本
- demo.less
- demo.html
- 非标准设计稿操作
- vw适配(百分比)
- 示例
- 普通写法示例
- vw + less写法示例
- demo.less
- demo01.html
- 1物理像素边框
- 示例
- 移动端事件
相关概念
(一)屏幕相关
1. 屏幕大小
指屏幕对角线长度,单位是英寸(inch)。常见尺寸有:3.5寸、4.0寸、5.0寸、5.5寸、6.0寸等。备注:1英寸(inch)=2.54厘米(cm)
2. 屏幕分辨率
是指屏幕在:横向、纵向上的物理像素点(屏幕上微小的发光点)总数,一般用 n * m 表示。
例如:iPhone6 的屏幕分辨率为: 750 * 1334
- 注意:
-
- 屏幕分辨率是一个固定值,屏幕生产出来就确定了,无法修改!!
-
- 屏幕分辨率、显示分辨率是两个概念,系统设置中可以修改的是:显示分辨率。
-
- 显示分辨率是设备当前所用到的物理像素点数,也可以说:屏幕分辨率 >= 显示分辨率
常见手机分辨率
3. 屏幕密度
又称:屏幕像素密度,是指屏幕上每英寸里包含的物理像素点个数,单位是ppi(pixels per inch),其实这里还有另一个单位 dpi (dots per inch),两个值的计算方式都一样,只是使用的场景不同。
ppi主要用来衡量屏幕,
dpi主要用来衡量打印机、投影仪等。
(二)像素相关
1.物理像素
又名:设备像素,物理像素是一个长度单位,单位是px,1个物理像素就是屏幕上的一个物理成像点,就是屏幕中一个微小的发光物理元器件(可简单理解为超级微小的灯泡),是屏幕能显示的最小粒度。屏幕的物理像素点数(分辨率)是手机屏幕的一个重要参数,由屏幕制造商决定,屏幕生产后无法修改。
例如iPhone6 横向上拥有的物理像素为750、纵向上拥有的物理像素为1334,用750*1334表示。
物理像素图示:
2. css 像素
css像素又名:逻辑像素,css像素是一个抽象的长度单位,单位也是px,它是为Web开发者创造的,用来精确地度量Web页面上的内容大小。我们在编写css\js\less中所使用的都是css像素(可以理解为:“程序员像素”)。
思考︰我代码中所写的1px (css像素),到了屏幕上到底对应几个物理像素呢?是1个css像素就对应1个物理像素(“发光的灯泡")吗?要探讨这个对应关系,就要学习接下来的新概念:设备独立像素。
3. 设备独立像素
设备独立像素简称DIP或DP(device-independent pixel),又称:屏幕密度无关像素(它由设备决定,我们无法改变)。
引言:在没出现【高清屏】的年代,1个css像素对应1个物理像素,但自从【高清屏】问世,二者就不再是1对1的关系了。苹果公司在2010年推出了一种新的显示标准:在屏幕尺寸不变的前提下,把更多的物理像素点压缩至一块屏幕里,这样分辨率就会更高,显示效果就会更佳细腻。苹果将这种屏幕称为: Retina屏幕(又名:视网膜屏幕),与此同时推出了配备这种屏幕的划时代数码产品—iPhone4。
我们来看一个场景:
程序员写了: width=2px, height=2px的盒子,若1个css像素直接对应1个物理像素,由于 iPhone3G/S与iPhone4屏幕尺寸相同,但iPhone4的屏幕能容纳下更多的物理像素点,所以iPhone4的物理像素点比iPhone3G/S小很多,那么理论上这个盒子在 iPhone4屏幕上也就会比iPhone3G/S屏幕上小很多,而事实是 iPhone3G/S和 iPhone4下这个盒子是一样大的!!!,只不过 iPhone4更加细腻、清晰。如何做到的呢?这就要靠设备独立像素。
设备独立像素的出现,使得即使在【高清屏】上元素也可以拥有正常的尺寸,s让代码不受到设备的影响,它是设备厂商根据屏幕特性设置的,无法更改。
设备独立像素与物理像素关系:
- 普通屏幕下:1 设备物理像素 = 1 物理像素
- 高清屏幕下:1 设备物理像素 = N 物理像素
设备独立像素与css像素关系:
- 在标准情况下(无缩放):1 css像素 = 1 设备独立像素
4.像素比
像素比(dpr,device pixel ratio):单一方向上,【物理像素】和【设备独立像素】的比值。即:dpr = 物理像素 / 设备独立像素
- 使用js获取dpr: window.devicePixelRatio
- 几款手机的屏幕像素参数,点击这里查看更多:
5.像素之间的关系
在不考虑缩放
的情况下(理想状态下):
- 普通屏(dpr = 1): 1css像素=1设备独立像素=1物理像素
- 高清屏(dpr = 2): 1css像素=1设备独立像素=2物理像素
- 高清屏(dpr = 3): 1css像素=1设备独立像素=3物理像素
程序员写了一个width为100px的盒子,那么:
- 1.代表100个css像素。
- 2.若用户不进行缩放,则对应100个设备独立像素。
- 3.在dpr为2的设备上,这100个css像素占据了100*2=200个物理像素(横向)。
描述一下你的屏幕,现在以iPhone6为例,我们描述一下屏幕(横向上):
- 1.物理像素: 750px
- 2.设备独立像素: 375px
- 3.css像素: 375px
(三)图片高清显示
位图像素
位图和矢量图
-
位图:又称点阵图形或栅格图像,是由n个的像素点(就是指位图的像素点,既不是物理像素,也不是css像素)组成的。放大后会失真。(常见:png、jpeg、jpg、gif),一般用PhotoShop等软件进行编辑。
-
矢量图:又称为面向对象图像或绘图图像,在数学上定义为一系列由线连接的点,放大后不会失真。(常见:svg),一般使用Adobe Illustrator,Sketch等软件进行编辑
位图像素也是一个长度单位,位图像素可以理解为位图中的一个“小格子”,是位图的最小单元。
注意:1个位图像素对应1个物理像素,图片才能得到完美清晰的展示。
具体编码时借助媒体查询:@media screen and (-webkit-min-device-pixel-ratio:x)
其实就是:假设将一个200*200px(这里指的是位图的像素点)的位图图片放入200px*200px的css像素盒子(这里指的是css盒子的大小)中,根据分辨率和物理像素比所得到的dpr,决定将此200*200px的图片使用多少实际的物理像素点显示。如果dpr是1,那么就是1css像素=1设备独立像素=1物理像素,那么这个位图的像素点数量正好可以塞满实际的物理像素点。但是如果是dpr为2,那么就是1css像素=1设备独立像素=2物理像素,位图像素的数量明显不够塞满实际的物理像素,所以会模糊填充,这样就会造成不能充分的利用好这样的高清屏幕。但是不管是使用的1x图,还是2x图,最终看的效果的大小是一样的,只是使用的图片的位图数量不够,导致图片看起来可能会不清晰。
图片的高清显示(媒体查询)
目前一般仅logo需要做高清显示,或提供svg格式logo即可解决问题,否则采用媒体查询:
/* 媒体查询的书写顺序也很重要 */
@media screen and (-webkit-min-device-pixel-ratio:2) {
.logo{
/* 可以直接使用content修改img标签中的src属性 */
content: url(../imgs/logo@2x.jpg)
}
}
@media screen and (-webkit-min-device-pixel-ratio:3) {
.logo{
content: url(../imgs/logo@3x.jpg)
}
}
(四) 视口相关
PC端视口
在pc端,视口的默认宽度和浏览器窗口的宽度一致。在css标准文档中,视口也被称作:初始包含块,它是所有css百分比宽度推算的根源,在PC端可通过如下几种方式获取宽度:
console.log('最干净的显示区域', document.documentElement.clientWidth); // 常用
console.log('最干净的显示区域+滚动条', window.innerWidth);
console.log('最干净的显示区域+滚动条+浏览器边框', window.outerWidth);
console.log('与浏览器无关,当前设备显示分辨率横向的值', screen.width);
注意:PC端只有一种视口(不像移动端有2个视口),PC端缩放页面可以通过window.onresize = ()=>{} 监测到。
移动端视口
在移动端,浏览器厂商面临着一个比较大的问题:他们如何将数以万计甚至可以说是数以亿计的pc端网页完整的呈现在移动端设备上,并且不会出现横向滚动条呢?那就要引出移动端的三个概念:1.布局视口、2.视觉视口、3.理想视口。
1. 布局视口 Layout viewport
为了解决早期的页面在手机上显示的问题,早期的时候我们这样做:pc端网页宽度一般都为:960px1024px这个范围,就算超出了该范围,960px~1024px这个区域也依然是版心的位置。
浏览器厂商针对移动端设备设计了一个容器,先用这个容器去承装PC端的网页,这容器的宽度一般是
980px,不同的设备可能有所差异,但相差并不大; 随后将这个容器等比例压缩到与手机等宽,这样就可以保证没有滚动条且能完整呈现页面,但是这样做依然有问题:网页内容被压缩的太小,严重影响用户体验。
移动端获取布局视口的方式,如下代码:
(注意:这在移动端使用此代码,获取的是布局视口,与在pc端使用此代码有所区别)
let layoutWidth = document.documentElement.clientWidth // (一般都是980px,iPad Pro是1024px)
注意:布局视口经过压缩后,横向的宽度用css像素表达就不再是375px了,而是980px,因为布局视口是被压缩,而不是截取(就是上面说的会将这个980px的容器等比例压缩到与手机等宽)。
示例
- 可以试想这个过程,先用布局视口宽度(可以通过document.documentElement.clientWidth获取到,可能不同设备,布局视口宽度会不一样)的容器,去接收整个网页(如果网页中的某个div需要铺满整个宽度,那么这个div的宽度的css像素就要设置为布局视口的宽度大小一样),然后,把这个接收完整个网页的容器按照宽度与移动设备的宽度相等作等比例缩放,再展示在移动端设备中。
- 前面说的iphone6为例它的css像素为375px是在
不考虑缩放的情况下
,但在此布局视口中,是会发生缩放的 - 我本机使用chrome浏览器显示的是错误的,但是使用edge浏览器是可以的,不知道啥原因,最好用自己的手机试一下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
<!-- 不要带下面这个meta标签 -->
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#info {
width: 980px;
background-color: pink;
/* 由于PC网页在移动端查看会压缩, 因此将字体放大 */
font-size: 50px;
}
</style>
</head>
<body>
<div id="info"></div>
</body>
<script>
// 获取移动端布局视口宽度
const layoutWidth = document.documentElement.clientWidth
const info = document.getElementById('info')
info.innerHTML = '您设备的布局视口宽度为' + layoutWidth + 'px(css像素)'
</script>
</html>
2.视觉视口 Visual viewport
视觉视口就是用户可见的区域,它的绝对宽度永远和设备屏幕一样宽,但是这个宽度里所包含的css像素值是变化的,例如:一般手机会将980个css像素放入视觉视口中 (在用户不缩放页面的情况下,视觉视口大小=布局视口大小
),而 ipad Pro会将1024个css像素放入视觉视口中。
移动端获取视觉视口方式: window.innerWidth
(这里不要和PC端使用此代码作对比,这里就仅仅是表示移动端的特殊含义:获取视觉视口),不过在 Android2、 Opera mini、UC8中无法正确获取。(一般也不通过代码看视觉视口)
如果以iPhone6为例,描述一下屏幕(横向上):
- 物理像素:750px(我们无法改变)
- 设备独立像素: 375px(我们无法改变)
- css像素:980px => 980px的布局视口被压缩进屏幕(980个css像素对接了375个设备独立像素)
3.理想视口 Visual viewport
与屏幕(设备独立像素,dp/dip)等宽的布局视口,称之为理想视口
,所以也可以说理想视口是一种标准:让布局视口宽与屏幕等宽(设备独立像素),靠meta标签实现。
理想视口的特点
- 布局视口和屏幕(设备独立像素,dp/dip)等宽,以 iPhone6为例,符合理想视口标准之后:设备独立像素:375px,布局视口宽度:375px
- 用户不需要缩放、滚动就能看到网站的全部内容。
- 要为移动端设备单独设计一个移动端网站。
设置理想视口的具体方法(这样设置后,不管是什么移动端设备,这个移动端设备的设备独立像素是多少,那么布局视口就是多少):
<meta name="viewport" content="width=device-width">
示例
- 对于下面的同样的一个页面,当开启理想视口后,对于iphone6是铺满宽度的,而对于iphone6plus却没有铺满整个宽度
- 假设在下面这个案例中,把meta开启理想视口的标签去掉,那么获取的布局视口在iphone6和iphone6plus都会是980px,这样只要写980px就能铺满整个宽度。当然,如果又换了其它机器去显示,可能就不一定是980px的布局视口了,可能是1024px,那么就要写1024px才能铺满这个机器的宽度了。
- 其实就是:在开启理想视口前,大多数设备的布局视口大多数为980px(可以通过document.documentElement.clientWidth获得);在开启理想视口后,任何设备的布局视口都是和这个设备的设备独立像素相等。
- 理想视口的那个meta标签理论上来说可加可不加,加了就是理想视口,不加就不是理想视口
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width">
<style>
* {
margin: 0;
padding: 0;
}
#info {
font-size: 50px;
width: 375px;
background-color: #bfa;
}
</style>
</head>
<body>
<div id="info"></div>
</body>
<script>
document.getElementById("info").innerHTML = '当前设备的布局视口宽度为: ' + document.documentElement.clientWidth;
</script>
</html>
【总结】
-
不写meta标签(不符合理想视口标准)
- 描述屏幕: (以iphone6为例)物理像素:750px、设备独立像素:375px、css像素:980px(一般都是980,可以通过document.documentElement.clientWidth获取)。
- 优点:元素在不同设备上,呈现效果几乎一样,因都是通过布局容器等比缩放的,例如200宽的盒子: 200/980
- 缺点:元素太小,页面文字不清楚,用户体验不好。
-
写meta标签(符合理想视口标准):
- 描述屏幕:物理像素:750px、设备独立像素:375px、css像素:375px。
- 优点:
- 页面清晰展现,内容不再小到难以观察,用户体验较好。
- 更清晰的像素关系:布局视口=视觉视口=设备独立像素=375px
- 更清晰的dpr 即:dpr = 物理像素 / 设备独立像素 = 物理像素 / css像素
- 例如:dpr = 2的设备,1 * 1 css像素 = 1 * 1 设备独立像素 = 2 * 2 物理像素
- 缺点:同一个元素,在不同屏幕(设备)上,呈现效果不一样,例如375宽的盒子:375/375和375/414(不是等比显示)
- 如何解决?做适配。
(五)缩放
PC端
放大时:
- 视口变小
- 元素的css像素值不变,但一个css像素所占实际面积变大了。
缩小时:
- 视口变大
- 元素的css像素值不变,但一个css像素所占实际面积变小了。
理解:pc端只有一种视口,pc端缩放时,比如:放大时,先把视口变小,然后元素按变小的视口排列,然后再把视口缩放成屏幕实际宽度,这样就达到了放大的效果。缩小时,先把视口变大,然后元素按变大的视口排列,然后再把视口缩放成屏幕实际宽度,这样就达到了缩小的效果。
小技巧:可以通过window.οnresize=()=>{}监测到PC端视口变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
img {
width: 200px;
height: 150px;
}
</style>
</head>
<body>
<img src="../imgs/1.jpg">
<img src="../imgs/2.jpg">
<img src="../imgs/3.jpg">
<img src="../imgs/4.jpg">
<script>
console.log('当前浏览器视口的宽度为: ', document.documentElement.clientWidth);
</script>
</body>
</html>
移动端
放大时:
- 布局视口不变
- 视觉视口变小(软件模拟手机与人眼距离变远,因此手机内视觉范围变小,内容更大)
缩小时:
- 布局视口不变
- 视觉视口变大(软件模拟手机与人眼距离变近,因此手机内视觉范围变大,内容更小)
注意:移动端缩放不会影响页面布局,因为缩放的时候,布局视口大小没有变化,简单记住:移动端的缩放没有改变布局视口的任何东西。而pc端缩放的时候,视口(pc端就一种视口)会变化,导致页面布局会产生变化。
形象理解:拿一个中间镂空的的纸片(镂空部分就相当于屏幕),挡在眼睛前面,去看前面的内容,当纸片离前面的内容越近看到的东西就越少,而镂空部分的大小又是不变的,因此看到的东西就会变大,类似于放大;缩小也就是镂空纸片离前面的内容越远
viewport
meta-viewport标签是苹果公司在2007年引进的,用于移动端布局视口的控制,企图改变980布局视口的行业规则。
viewport相关选项
- width 布局视口宽度
- initial-scale 系统初始缩放比例
- maximum-scale 允许用户缩放的最大比例 【safari不支持】
- minimum-scale 允许用户缩放的最小比例
- user-scalable 是否允许用户缩放 【safari不支持】
- viewport-fit 设置为cover值可以解决刘海屏的留白问题
1. width
width值可以是device-width(让布局视口的宽度等于设备独立像素值),也可以是具体值,但有些安卓手机不支持具体值,IOS全系列支持
示例
- 在chrome浏览器上,如果不写<meta name=“viewport” content=“width=device-width”>这个标签,它也是iphone678是375,iphone678plus是414。加上<meta name=“viewport” content=“width=device-width”>这个标签,它也是iphone678是375,iphone678plus是414。
- 而edge浏览器,如果不写<meta name=“viewport” content=“width=device-width”>这个标签,它也是iphone678、iphone678plus都是980。写上<meta name=“viewport” content=“width=device-width”>标签,它是iphone678是375,iphone678plus是414
- ios的所有机型都是可以直接设置width的值,且都能生效,部分安卓机不能直接设置具体值。
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 添加上此标签, 指定widthwei device-width -->
<meta name="viewport" content="width=device-width">
<title>Document</title>
</head>
<body>
</body>
<script>
console.log('布局视口宽度为: ' + document.documentElement.clientWidth);
</script>
</html>
2. initial-scale
- 页面初始化时的缩放比例
- initial-scale = 屏幕宽度(设备独立像素,dip)/ 布局视口宽度
- 只写 initial-scale = 1.0 也可以实现完美视口(即理想视口;根据上面的等式,其中设备独立像素由设备决定-无法改变,即可推出来设备独立像素值等于布局视口宽度值,因此就是理想视口),但为了更好的兼容性,一般 width=device-width, initial-scale=1.0一起写。
- 如果根据initial-scale和width的得到的布局视口值不一致,那么布局视口的宽度以其中大的为准
3. maximum-scale
- 允许用户缩放的最大比例 【safari不支持】
- maximum-scale = 屏幕宽度(设备独立像素,dip)/ 视觉视口宽度
- 前面我们知道了,在移动端,放大时,视觉视口会变小
- 也就是:视觉视口宽度>= 屏幕宽度(设备独立像素,dip)/ maximum-scale,因此视觉视口宽度会有一个最小值(视觉视口宽度不能比这更小了)
4. minimum-scale
- 允许用户缩放的最小比例
- minimum-scale = 屏幕宽度(设备独立像素,dip)/ 视觉视口宽度
- 前面我们知道了,在移动端,缩小时,视觉视口会变大
- 也就是:视觉视口宽度<= 屏幕宽度(设备独立像素,dip)/ maximum-scale,因此视觉视口宽度会有一个最大值(视觉视口宽度不能比这更大了)
5. user-scalable
是否允许用户通过手指缩放页面 【safari不支持】
6. viewport-fit
设置为cover可以解决【刘海屏】的留白问题
写法
移动端如下设置viewport:开启理想视口,禁止缩放
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
适配
为什么要做适配?
由于移动端设备的屏幕尺寸大小不一,会出现:同一个元素,在两个不同的手机上显示效果不一样(比例不同)。要想让同一个元素在不同设备上,显示效果一样,就需要适配,无论采用何种适配方式,中心原则永远是:等比!
主流的适配方式有三种:
- viewport适配(比较简单,但bug比较多)
- rem适配(主流方式,几乎完美适配)
- vw适配(未来趋势,写法简单,兼容性不太好)
问题演示
比如有一个设计稿是,可以看到是在iponhe6(设备独立像素宽度为375px)的条件下设计的
- 开启了理想视口
- 但是在iphone678是看起来正常的,但是换成了iphone678plus看起来就不太对(左右两边的宽度变宽了,没有按照iphone6的比例变化)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
}
.demo {
width: 345px;
height: 150px;
margin: 0 auto;
margin-top: 15px;
background-color: #a2bbee;
}
</style>
</head>
<body>
<div class="demo"></div>
</body>
</html>
1.viewport适配
- 方法:拿到设计稿之后,设置布局视口宽度为设计稿宽度,然后直接按照设计稿给宽高进行布局即可
- 优点:不用复杂的计算,直接使用图稿上标注的px值
- 缺点:
- viewport设置具体的width值,会导致在某些安卓手机上有兼容性问题
- 不希望适配的东西,例如边框,也强制参与了适配。
- 图片会失真
示例
- 比如:设计稿是在375px下设计的,那么就将width=“375”
- 在不同机型下,这个1px的边框在iphone6看上去是正常的,但是在ipad pro的屏幕看上去变粗了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 比如:设计稿是在375px下设计的,那么就将width="375" -->
<meta name="viewport" content="width=375">
<title>viewport适配</title>
<style>
* {
margin: 0;
padding: 0;
}
.demo {
width: 345px;
height: 150px;
margin: 0 auto;
margin-top: 15px;
background-color: #a2bbee;
border: 1px solid #000;
}
</style>
</head>
<body>
<div class="demo"></div>
</body>
</html>
2.rem适配
em和rem介绍
em 和 rem 都是 css 中的长度单位。而且两个都是相对长度单位,不过两个有点区别
- 1em 相对的是当前所设置的元素的所指定的字体大小的1倍(如果当前元素未指定字体大小,那么就找父元素,一直到根元素-css继承特性,根元素的字体大小可以在浏览器设置中设置,chrome默认是16px)
- 1rem 相对的是根元素所设置的字体大小的1倍(根元素的字体大小可以在浏览器设置中设置,chrome默认是16px,也可以通过代码(html,body{font-size: 30px;})设置)
- 比如html,body
rem适配的原理:编写样式时统一使用rem为单位,在不同设备上动态调整根字体大小
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>em和rem</title>
<style>
* {
margin: 0;
padding: 0;
}
html,body {
/* 设置根元素字体大小 */
font-size: 30px;
}
.test1 {
width: 400px;
height: 400px;
background-color: pink;
}
.test2 {
width: 10em;
height: 10rem;
/* 设置当前元素的字体的大小 */
font-size: 20px;
background-color: #bfa;
}
</style>
</head>
<body>
<div class="test1">
<div class="test2"></div>
</div>
</body>
</html>
rem方案一:核心原理
淘宝、百度的移动端页面用的此方案
理想视口下
(注意:此方案需要先设置理想视口),以设计稿(dip为375px)为标准,根字体设计为100px,来匹配其它设备的dip
- 根字体大小 = 手机横向设备独立像素值 / 设计稿宽度 * 100 px
- 编写样式时:
- 直接以rem为单位
- 值 = 设计稿值 / 100
推算过程
- 第一步,根据设计稿的375px(因此设计师使用的是iphone6机型为样板),我们假定把根字体设置为100px(这个字体大小不要给的小于12px,否则会不起作用),那么所有需要适配的宽度除以100即可(因此,取100是为了方便计算,直接挪动小数点即可),然后把rem作为单位
- 第二步,用户换了另外一个机型,比如iphone678plus,按照比例关系设计稿的375px,需要变成414px,也就是需要扩大414/315倍=1.104,就相当于所有的尺寸都需要依据此比例关系扩大,而所写的代码全部使用的是rem,因此,此时只需要改变根字体大小即可,让根字体大小扩大414/315倍即可,即根字体设置为: 100 * 414 / 315 px即可
示例
- 第一步需要开启理想视口,获得:布局视口=设备横向独立像素值 的等式关系,以此获取到设备横向独立像素值
- 根据设置好的比例关系将设计稿的数值换算成rem(做适配),对于不需要适配的情况不要用rem(直接使用px)
- 看下面的效果图,在不同的机型下,两边的空白区域和上边是等比例的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 第一步:开启理想视口 -->
<meta name="viewport" content="width=device-width">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#demo {
/* (未适配前的书写的代码)
width: 345px;
height: 150px;
margin: 0 auto;
margin-top: 15px;
background-color: #a2bbee; */
/* 第二步:换算成rem */
width: 3.45rem;
height: 1.5rem;
margin: 0 auto;
margin-top: 0.15rem;
background-color: #a2bbee;
/* 对于不需要适配的情况,就不要使用rem了,而是使用px */
border: 1px solid black;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
<script>
function adapter() {
// 获取布局视口宽度,因为开启了理想视口(因此第一步就需要开启理想视口),就会有这个等式关系:布局视口=设备横向独立像素值
const dpWidth = document.documentElement.clientWidth // 因此这里拿到布局视口的宽度就是设备横向独立像素值
// 计算根字体大小(这是根据比例关系计算而来)
const rootFontsize = (dpWidth * 100)/375
// 设置根字体大小
document.documentElement.style.fontSize = rootFontsize + 'px'
}
adapter()
// 监听布局视口的变化(当通过浏览器控制台切换不同机型时,实时的适配)
window.onresize = adapter
</script>
</html>
rem方案二:核心原理
理想视口下,根字体设为dip/10,即把dip比作10份rem,考虑内部元素的占多少rem
- 根字体大小 = 手机横向设备独立像素值 / 10
- 编写样式时:
- 直接以rem为单位
- 值 = 设计稿值 * (10/dip)
- 该方案结合less将无敌(在vscode下,可以使用Easy Less插件,只要保存.less文件,就会自动生成对应的.css文件)
示例
- 与rem方案一的区别,就在于rem方案二是在开启理想视口的情况下,就直接把设备独立像素值除以10的值,将这个值就作为根元素的字体大小值,然后,需要适配的尺寸都按照这个值来换算,比如150px(150除以37.5=4,因此就是4rem),效果与rem方案一是一样的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 第一步:开启理想视口 -->
<meta name="viewport" content="width=device-width">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#demo {
/* (未适配前的书写的代码)
width: 345px;
height: 150px;
margin: 0 auto;
margin-top: 15px;
background-color: #a2bbee; */
/* 第二步:换算成rem */
width: 9.2rem;
height: 4rem;
margin: 0 auto;
margin-top: 0.4rem;
background-color: #a2bbee;
/* 对于不需要适配的情况,就不要使用rem了,而是使用px */
border: 1px solid black;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
<script>
function adapter() {
// 获取布局视口宽度,因为开启了理想视口(因此第一步就需要开启理想视口),就会有这个等式关系:布局视口=设备横向独立像素值
const dpWidth = document.documentElement.clientWidth // 因此这里拿到布局视口的宽度就是设备横向独立像素值
// 计算根字体大小(这是根据比例关系计算而来)
const rootFontsize = dpWidth / 10
// 设置根字体大小
document.documentElement.style.fontSize = rootFontsize + 'px'
}
adapter()
window.onresize = adapter
</script>
</html>
rem方案二 + less版本
demo.less
- 需要加小括号才会去计算
* {
margin: 0;
padding: 0;
}
@font: (375/10rem);
#demo{
width: (345/@font);
height: (150/@font);
margin: 0 auto;
margin-top:(15/@font);
background-color: #a2bbee;
/* 对于不需要适配的情况,就不要使用rem了,而是使用px */
border: 1px solid black;
}
demo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 第一步:开启理想视口 -->
<meta name="viewport" content="width=device-width">
<title>Document</title>
<link rel="stylesheet" href="./demo.css">
</head>
<body>
<div id="demo"></div>
</body>
<script>
function adapter() {
// 获取布局视口宽度,因为开启了理想视口(因此第一步就需要开启理想视口),就会有这个等式关系:布局视口=设备横向独立像素值
const dpWidth = document.documentElement.clientWidth // 因此这里拿到布局视口的宽度就是设备横向独立像素值
// 计算根字体大小(这是根据比例关系计算而来)
const rootFontsize = dpWidth / 10
// 设置根字体大小
document.documentElement.style.fontSize = rootFontsize + 'px'
}
adapter()
window.onresize = adapter
</script>
</html>
非标准设计稿操作
上面的示例是按照375,即iphone6作为样板设计的。如果设计稿不是375,则按照上面的方法(看上面图中的核心过程),按比例计算即可(需要适配的尺寸按上面的方法计算,js代码只需要改动对应的地方的尺寸即可)。
vw适配(百分比)
vw和vh是两个相对单位
- 1vw =等于布局视口宽度的1%
- 1vh=等于布局视口高度的1%
不过vw和vh有一定的兼容性问题:详见:这里
所以我们可以代码大概可以怎么写?
- 按照当前设计图给出的屏幕宽度(布局视口宽度)算出每一个容器所占据的百分比后用vw来替换(注意,是vw!vw!vw!)
- 为什么要强调是vw呢? 因为假如你使用vh,1vh是布局视口高度的1%,可是设计稿按等比例放入到手机屏幕中,肯定是按照宽度来等比的,高度可以使用滚动条的方式向下滑动,所以用vh就不合适了
示例
- 不需要使用js脚本获取设备独立像素了
- 效果能够适配,但是兼容性不佳
普通写法示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 开启理想视口 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#demo {
background-color: hotpink;
/* width: 345px;
height: 150px;
margin: 15px; */
/* 对于需要适配的尺寸,就看这个设计尺寸占宽度的百分比 */
/* 345/375 *100 = 92 */
width: 92vw;
/* 150/375 *100 = 40 */
height: 40vw;
/* 15/375 *100 = 4 */
margin: 4vw;
margin-bottom: 0;
/* 对于不需要适配的尺寸,直接写px单位 */
border: 1px solid black;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
</html>
vw + less写法示例
demo.less
@vwSize: 375/100vw;
* {
margin: 0;
padding: 0;
}
#demo {
background-color: hotpink;
/* width: 345px;
height: 150px;
margin: 15px; */
width:(345/@vwSize);
height: (150/@vwSize);
margin: (15/@vwSize);
margin-bottom: 0;
border: 1px solid black;
}
demo01.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./demo.css">
</head>
<body>
<div id="demo"></div>
</body>
</html>
1物理像素边框
在dpr为2或者3以上的情况下,1css像素对应2个或者3个物理像素(说通俗点就是高dpr下,1px的css像素所显示的效果并没是我们想象的1px效果)
默认情况下的1pxcss像素的显示效果
放大后的1px css像素的显示效果,可以明显看到边框肯定不是1px
设置物理像素边框适配后的效果,可以看到,即使在搞dpr下,边框也依旧为1px
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#demo {
width: 150px;
height: 150px;
/* background-color: hotpink; */
}
/* 关键代码就是媒体查询
dpr为2的情况下,就将border从1px设置为0.5px
这样子0.5px的css像素 只能点亮1px物理像素(0.5*2) */
@media screen and (-webkit-min-device-pixel-ratio: 2) {
#demo {
border: 0.5px solid black;
}
}
/* dpr为3的情况下,就将border从1px设置为0.33px
这样子0.33px的css像素 只能点亮1px物理像素(0.33*3) */
@media screen and (-webkit-min-device-pixel-ratio: 3) {
#demo {
border: 0.33px solid black;
}
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
</html>
移动端事件
事件类型
移动端事件列表
- touchstart 元素上触摸开始时触发
- touchmove 元素上触摸移动时触发
- touchend 手指从元素上离开时触发
- touchcancel 触摸被打断时触发(如弹窗、来电等)
以上事件最早出现于IOS safari中,为了向开发人员转达一些特殊的信息
应用场景
- touchstart 事件可用于元素触摸的交互,比如页面跳转,标签页切换
- touchmove 可用于页面的滑动特效,网页游戏,画板
- touchend 主要跟touchmove事件结合使用
- touchcancel 使用率不高
注意:
- touchmove事件触发后,即使手指离开了元素,touchmove事件也会持续触发
- 触发touchmove与touchend事件,一定要先触发touchstart
- 事件的作用在于实现移动端的界面交互
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#demo {
width: 100%;
height: 350px;
background-color: hotpink;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
<script>
let demo = document.querySelector('#demo')
demo.addEventListener('touchstart',()=>{
console.log('你手指触摸到粉色的盒子-touchstart');
})
demo.addEventListener('touchmove',()=>{
console.log('你手指在粉色的盒子上移动了-touchmove');
})
demo.addEventListener('touchend',()=>{
console.log('你手指在粉色的盒子上移开了-touchend');
})
demo.addEventListener('touchcancel',()=>{
console.log('触摸被打断了-touchcancel');
})
</script>
</html>
移动端事件对象
- touches: 屏幕上的触点数(是个伪数组)
- targetTouches: 当前元素上的触点数
- changedTouches: 屏幕上改变了的触点数(也就是:同时按下几个手指)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#demo {
width: 100%;
height: 350px;
background-color: hotpink;
}
</style>
</head>
<body>
<div id="demo"></div>
</body>
<script>
/*
touches: 屏幕上的触点数(是个伪数组)
targetTouches: 当前元素上的触点数
changedTouches: 屏幕上改变了的触点数(也就是:同时按下几个手指)
*/
let demo = document.querySelector('#demo')
demo.addEventListener('touchstart',(e)=>{
// 阻止选择文字这一默认行为
e.preventDefault()
demo.innerHTML = '屏幕上的触点数: ' + e.touches.length + '<br/>'
demo.innerHTML += 'demo盒子上的触点数: ' + e.targetTouches.length + '<br/>'
demo.innerHTML += '屏幕上改变了的触点数: ' + e.targetTouches.length
})
</script>
</html>
点击穿透
touchstart & click
touch事件结束后会默认触发元素的click事件
(同时给元素绑定click事件和touchstart事件,会先触发touchstart事件,然后过一个时间间隔,再触发click事件),如果没有设置完美视口,则事件触发的时间间隔为300ms左右,如设置完美视口则时间间隔为30ms左右(视具体设备特性而定)。
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.demo {
width: 100px;
height: 100px;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="demo">demo</div>
</body>
<script>
let demo = document.querySelector('.demo')
let time = null
demo.addEventListener('click',()=>{
let now = Date.now()
console.log('click事件发生了',now, now-time);
})
demo.addEventListener('touchstart',()=>{
time = Date.now();
console.log('touchStart事件发生了',time);
})
</script>
</html>