前端-CSS学习笔记18-移动端适配

媒体查询@media,像素,视口,移动端适配方案[rem+动态html的font-size,vw,flex]

1. 媒体查询

  • 媒体查询是一种提供给开发者针对不同设备需求进行定制化开发的一个接口

  • 可以根据设备的类型(比如屏幕设备、打印机设备)或者特定的特性(比如屏幕的宽度)来修改页面

  • 媒体查询的使用方式主要有三种

    • 方式一:通过@media和@import使用不同的CSS规则(常用)

      1
      2
      3
      <style>
      @import url("./css/miniScreen.css") (max-width:600px);
      </style>
    • 方式二:使用media属性为 style,link,source和其他HTML元素指定特定的媒体类型

      1
      <link rel="stylesheet" media="(max-width:600px)" href="./css/miniScreen.css">
    • 方式三:使用Window.matchMedia() 和MediaQueryList.addListener() 方法来测试和监控媒体状态

  • 比较常用的是通过@media来使用不同的CSS规则

    • 语法
    1
    2
    3
    @media mediatype and|not|only (media feature){
    css-code;
    }

1.1. 媒体类型 mediatype

  • 在使用媒体查询时,必须指定要使用的媒体类型

    • 媒体类型是可选的,并且会(隐式地)应用 all 类型
  • 常见的媒体类型值

    说明
    all 所有设备
    print 打印机和打印预览
    screen 电脑屏幕,平板电脑,智能手机等
    speech 语音合成器
  • 被废弃的媒体类型

    • CSS2.1 和 Media Queries 3 定义了一些额外的媒体类型(tty, tv, projection, handheld, braille, embossed, 以及 aural)
    • 但是在Media Queries 4 中已经被废弃,并且不应该被使用
    • aural类型被替换为具有相似效果的speech

1.2. 媒体特性media feature

  • 媒体特性描述了 浏览器、输出设备,或是预览环境的具体特征
    • 通常会将媒体特性描述为一个表达式
    • 每条媒体特性表达式都必须用括号括起来
特征 价值 支持最小/最大 描述
宽度width 长度 是的 渲染表面的宽度
高度height 长度 是的 渲染表面的高度
颜色color 整数 是的 每个颜色分量的位数
设备比例device-aspect-ratio 整数/整数 是的 长宽比
设备高度device-width 长度 是的 输出设备的高度
设备宽度device-height 长度 是的 输出设备的宽度
方向orientation portrait/landscape 屏幕方向
分辨率resolution dpi/dpcm/dppx 是的 解析度

1.3. 逻辑操作符and|not|only|,

  • 媒体查询的表达式最终会获得一个boolean值,也就是真(true)或者假(false)

    • 如果结果为真(true),那么就会生效
    • 如果结果为假(false),那么就不会生效
  • 如果有多个条件,可以通过逻辑操作符联合复杂的媒体查询

    关键字 说明
    and and 操作符用于将多个媒体查询规则组合成单条媒体查询
    not not运算符用于否定媒体查询,如果不满足这个条件则返回true,否则返回false
    only only运算符仅在整个查询匹配时才用于应用样式
    , 逗号用于将多个媒体查询合并为一个规则
  • 应用:@media 不同分辨率样式

    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
    /*屏幕宽度大于500,小于700的时候,body背景颜色为红色*/
    @media screen and (min-width: 500px) and (max-width: 700px){
    body {
    background-color: red;
    }
    }

    /*仅针对屏幕的媒体宽度小于等于240像素的页面h1元素显示为14像素*/
    @media only screen and (max-device-width: 240px){
    h1{
    font-size:14px;
    }
    }

    /*适配特定长宽比*/
    @media only screen and (device-aspect-ratio:4/3){
    h1{
    font-size:14px;
    }
    }

    @media only screen and (min-width: 320px) {
    /* 􏳆非Retina屏幕􏼉􏼊,􏼉􏼊􏻇􏽈􏱤􏲼􏲽􏲼屏幕宽度大于等于320像素􏳇􏼴 */
    background:url(icon.png) no-repeat;
    background-size:100px 100px;
    }

    /*在Retina􏼉􏼊中一个点则等于两个像素*/
    @media
    only screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: 320px), only screen and (min-device-pixel-ratio: 2) and (min-width: 320px),
    only screen and (/*分辨率为192dpi以上*/min-resolution: 192dpi) and (min-width: 320px),
    only screen and (/*分辨率为2dppx以上*/min-resolution: 2dppx) and (min-width: 320px) {
    /* 􏳆Retina屏幕􏼉􏼊,􏼉􏼊􏻇􏽈􏱤􏲼􏲽􏲼屏幕宽度大于等于320像素􏳇􏼴 */
    background:url(icon@2x.png) no-repeat;
    background-size:100px 100px;
    }

2. CSS中的单位

  • 经常使用px来表示一个长度(大小),比如font-size设置为18px,width设置为100px

    • px是一个长度(length)单位,事实上CSS中还有非常多的长度单位
  • 整体可以分成两类

    • 绝对长度单位(Absolute length units)

    • 相对长度单位(Relative length units)

2.1. CSS中的绝对单位

  • 绝对单位(Absolute length units)
    • 与其他任何东西都没有关系,通常被认为总是相同的大小
    • 这些值中的大多数在用于打印时比用于屏幕输出时更有用,例如,通常不会在屏幕上使用cm
    • 唯一经常使用的值,就是px(像素)
绝对单位 名称 等价换算
cm 厘米 1cm=96px/2.54
mm 毫米 1cm=1/10th of 1cm
Q 四分之一毫米 1Q=1/40th of 1cm
in 英寸 1in=2.54cm=96px
pc 十二点活字 1pc=1/16th of 1in
pt 1pt=1/72th of 1in
px 像素 1px=1/96th of 1in
Recommended Occasional use Not recommended
Screen em,px,% ex pt,cm,mm,in,pc
Print em,cm,mm,in,pt,pc,% px,ex

2.2. CSS中的相对单位

  • 相对长度单位
    • 相对长度单位相对于其他一些东西
    • 比如父元素的字体大小,或者视图端口的大小
    • 使用相对单位的好处是,经过一些仔细的规划,可以使文本或其他元素的大小与页面上的其他内容相对应
相对单位 相对于
em 在font-size中使用是相对于父元素的字体大小
在其他属性中使用是相对于自身的字体大小,如width
ex 字符x的高度
ch 数字0的宽度
rem html根元素的字体大小
lh 元素的line-height
vw 视窗宽度的1%
vh 视窗高度的1%

3. 像素

  • 一直在使用px单位,px是pixel单词的缩写,翻译为像素

  • 那么像素到底是什么?

    • 像素是影响显示的基本单位(比如屏幕上看到的画面、一幅图片)
    • pix是英语单词picture的常用简写,加上英语单词“元素”element,就得到pixel
    • “像素”表示“画像元素”之意,有时亦被称为pel(picture element)

3.1. 像素的不同分类

  • 但是这个100个pixel到底是多少呢?

    • 可以在屏幕上看到一个大小,但是这个大小代表的真实含义是什么呢?
    • 经常说一个电脑的分辨率、手机的分辨率,这个CSS当中的像素又是什么关系呢?
  • 深入到不同的像素概念中,来理解CSS中的pixel到底代表什么含义

  • 像素单位常见的有三种像素名称

    • 设备像素(也称之为物理像素)
    • 设备独立像素(也称之为逻辑像素)
    • CSS像素

3.2. 物理像素

  • 设备像素,也叫物理像素

    • 设备像素指的是显示器上的真实像素,每个像素的大小是屏幕固有的属性,屏幕出厂以后就不会改变了

    • 在购买显示器或者手机的时候,提到的设备分辨率就是设备像素的大小

    • 比如iPhone X的分辨率1125x2436,指的就是设备像素

3.3. 逻辑像素

  • 设备独立像素,也叫逻辑像素
    • 如果面向开发者使用设备像素显示一个100px的宽度,那么在不同屏幕上显示效果会是不同的
    • 开发者针对不同的屏幕很难进行较好的适配,编写程序必须了解用户的分辨率来进行开发
    • 所以在设备像素之上,操作系统为开发者进行抽象,提供了逻辑像素的概念
    • 比如购买了一台显示器,在操作系统上是以1920x1080设置的显示分辨率,那么无论购买的是2k、4k的显示器,对于开发者来说,都是1920x1080的大小

3.4. CSS像素

  • CSS中经常使用的单位也是pixel,在默认情况下等同于设备独立像素(也就是逻辑像素)

    • 毕竟逻辑像素才是面向开发者的
  • 可以通过JavaScript中的screen.width和screen.height获取到电脑的逻辑分辨率

3.5. DPR、PPI

  • DPR:物理像素比(device pixel ratio)

    • 2010年,iPhone4问世,不仅仅带来了移动互联网,还带来了Retina屏幕
    • Retina视网膜屏幕显示技术的出现,将更多的物理像素点压缩至一块屏幕里,从而达到更高的分辨率,并提高屏幕显示的细腻度
    • 一个px能显示的物理像素点的个数,称为物理像素比或屏幕像素比
    • 在Retina屏幕中,一个逻辑像素在长度上对应两个物理像素
    • 可以通过window.devicePixelRatio获取到当前屏幕上的DPR值
  • PPI:每英寸像素(Pixels Per Inch)

    • 通常用来表示一个打印图像或者显示器上像素的密度
    • 1英寸=2.54厘米,在工业领域被广泛应用

3.6. 常见移动端设备

  • PC端开发的1px等于一个物理像素,移动端开发的1px不等于一个物理像素

  • 以iPhone为例

    手机机型(iPhone) 屏幕尺寸(inch) 逻辑分辨率(pt) 设备分辨率 (px) 物理像素比dpr
    3G(s) 3.5 320x480 320x480 1x
    4(s) 3.5 320x480 640x960 2x
    5(s/se) 4 320x568 640x1136 2x
    6(s)/7/8 4.7 375x667 750x1334 2x
    X/xs/11 Pro 5.8 375x812 1125x2436 3x
    6(s)/7/8 Plus 5.5 414x736 1242x2208 3x
    Xr/11|6.1 6.1 414x896 828x1792 2x
    Xs Max/11 Pro Max 6.5 414x896 1242x2688 3x

3.7. 二倍图

  • 对于一张50x50的图片,在手机Retina屏中打开,按照移动端物理像素比会放大倍数,会造成图片模糊
  • 采取放一个100x100的图片,手动将图片缩小为50x50
1
<img src="img100.jpg" style="width:50px;height:50px;">

切图工具 http://www.cutterman.cn/zh/cutterman

4. 移动端开发

4.1. 当前形势

  • 移动互联网的快速发展,习惯于使用手机来完成大部分日常的事务

  • 移动端开发目前主要包括三类

    • 原生App开发(iOS、Android、RN、uniapp、Flutter等)
    • 小程序开发(原生小程序、uniapp、Taro等)
    • Web页面(移动端的Web页面,可以使用浏览器或者webview浏览)
  • 这里有两个概念

    • 自适应:根据不同的设备屏幕大小来自动调整尺寸、大小
    • 响应式:会随着屏幕的实时变动而自动调整,是一种自适应

4.2. 开发技术选择

  • 单独移动端页面(主流)

    • 网址域名前面加m(mobile)可以打开移动端,通过判断设备,跳转至移动端页面
  • 响应式页面兼容移动端(其次)

    • 判断屏幕宽度来改变样式,以适应不同终端

    • 需要花很大精力去调兼容性问题

单独制作移动端页面 响应式页面兼容移动端
京东商城手机版 三星手机官网
淘宝触屏版
苏宁易购手机版

总结 目前市场主流的选择还是单独制作移动端页面

4.3. 移动端技术解决方案

  • 移动端浏览器基本以webkit内核为主,因此就考虑webkit兼容性问题
  • 可以放心使用h5和css3样式
  • 同时浏览器的私有前缀只需要考虑添加webkit即可

4.4. 初始化 normalize.css

4.5. 移动端样式清除

1
2
3
4
5
6
/* 清除点击高亮 */
-webkit-tap-highlight-color: transparent;
/* ios上使按钮和输入框可以自定义样式 */
-webkit-appearance: none;
/* 禁用长按页面时弹出菜单 */
-webkit-touch-callout: none;

5. 视口viewport

  • 视口概念

    • 在一个浏览器中,可以看到的区域就是视口(viewport)
    • fixed就是相对于视口来进行定位的
    • 在PC端的页面中,是不需要对视口进行区分,因为布局视口和视觉视口是同一个
  • 但是在移动端,布局的视口和可见的视口是不太一样的

    • 这是因为移动端的网页窗口往往比较小,可能会希望一个大的网页在移动端可以完整的显示
    • 所以在默认情况下,移动端的布局视口是大于视觉视口的
  • 在移动端,可以将视口划分为三种情况

    • 布局视口(layout viewport)
    • 视觉视口(visual layout)
    • 理想视口(ideal layout)
  • 这些概念的区分来自ppk,他对前端贡献比较大的一个人(特别是在移动端浏览器)

5.1. 布局视口(layout viewport)

  • 默认情况下,一个在PC端的网页在移动端会如何显示呢?

    • 一般移动设备的浏览器都默认设置了一个布局视口,用于解决早期的PC端页面在手机上显示的问题
    • IOS,Android基本都将这个视口分辨率设置为 980px,所以PC上的网页大多都能在手机上呈现,只不过元素看上去很小,一般默认可以通过手机缩放网页
  • 相对于980px布局的这个视口,称之为布局视口(layout viewport)

    • 布局视口的默认宽度是980px

5.2. 视觉视口(visual viewport)

  • 如果默认情况下,按照980px显示内容,那么右侧有一部分区域就会无法显示,所以手机端浏览器会默认对页面进行缩放以显示到用户的可见区域中

    • 那么显示在可见区域的这个视口,就是视觉视口(visual viewport)
  • 在Chrome上按shift+鼠标左键可以进行缩放

5.3. 理想视口(ideal viewport)

  • 如果所有的网页都按照980px在移动端布局,那么最终页面都会被缩放显示

    • 事实上这种方式是不利于进行移动开发的,希望的是设置100px,那么显示的就是100px
    • 通过设置理想视口(ideal viewport)实现这一点
  • 默认情况下的layout viewport并不适合进行布局

    • 可以对layout viewport进行宽度和缩放的设置,以满足正常在一个移动端窗口的布局
    • 这个时候可以设置meta中的viewport
  • meta视口标签

    1
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
    可能的附加值 描述
    width 一个正整数,或者字符串 device-width 定义 viewport 的宽度
    height 一个正整数,或者字符串 device-height 定义 viewport 的高度。未被任何浏览器使用
    initial-scale 一个 0.0 和 10.0 之间的正数 定义设备宽度与 viewport 大小之间的缩放比例
    maximum-scale 一个 0.0 和 10.0 之间的正数 定义缩放的最大值,必须大于等于 minimum-scale,否则表现将不可预测
    minimum-scale 一个 0.0 和 10.0 之间的正数 定义缩放的最小值,必须小于等于 maximum-scale,否则表现将不可预测
    user-scalable yes 或者 no 默认为 yes,如果设置为 no,将无法缩放当前页面。浏览器可以忽略此规则

6. 移动端适配方案

  • 移动端的屏幕尺寸通常是非常繁多的,很多时候希望在不同的屏幕尺寸上显示不同的大小

  • 比如设置一个100x100的盒子

    • 在375px的屏幕上显示是100 x 100
    • 在320px的屏幕上显示是90+ x 90+
    • 在414px的屏幕上显示是100+ x 100+
  • 其他尺寸也是类似,比如padding、margin、border、left,甚至是font-size等等

    • 这个时候,需要一些方案来处理尺寸
  • 方案一:百分比设置

    • 因为不同属性的百分比值,相对的可能是不同参照物,所以百分比往往很难统一
    • 所以百分比在移动端适配中使用是非常少的
  • 方案二:rem单位+动态html的font-size

  • 方案三:vw单位

  • 方案四:flex的弹性布局

  • 方案五:响应式布局

7. 适配方案-流式布局(百分比布局)

  • 通过盒子的宽度设置成百分比来根据屏幕的宽度来进行伸缩,不受固定像素的限制,内容向两边填充
  • 一般设定最大最小宽高限制
    • max-width 最大宽度,max-height最大高度
    • min-width 最小宽度,min-height最小高度
  • 布局应用:京东商城 https://m.jd.com/
1
2
3
4
5
6
.box{
width: 50%;
height: 400px;
max-width: 980px;
min-width: 320px;
}

8. 适配方案-rem+动态html的font-size

  • 让一些不能等比例自适应的元素,达到当设备尺寸发生改变的时候,等比例适配当前设备

  • rem单位是相对于html元素的font-size来设置的,如果需要在不同的屏幕下有不同的尺寸,可以动态的修改html的 font-size 尺寸

  • 当html字体大小变化元素尺寸也会发生变化,从而达到等比缩放的适配

  • 比如如下案例

    1. 设置一个盒子的宽度是1rem
    2. 设置不同的屏幕上html的font-size不同
    屏幕尺寸 html的font-size 盒子的设置宽度 盒子的最终宽度
    375px 37.5px 1rem 37.5px
    320px 32px 1rem 32px
    414px 41.4px 1rem 41.4px
  • 这样在开发中,只需要考虑两个问题

    • 问题一:针对不同的屏幕,设置html不同的font-size
    • 问题二:将原来要设置的尺寸,转化成rem单位

8.1. 适配方案

  1. 按照设计稿与设备宽度的比例,动态计算并设置html根标签的font-size大小
  2. css中,设计稿元素的宽,高,相对位置等取值,按照同等比例换算为rem单位的值

8.1.1. 设计稿常见尺寸宽度

设备 宽度
iphone 4/5 640px
iphone 6/7/8 750px
常见320px,360px,384px,400px,414px,500px,720px,大部分4.7~5寸的安卓设备为720px

一般情况下,以一套或两套效果适应大部分的屏幕,放弃极端屏,牺牲一些效果现在基本以750px为准

8.1.2. 设计html的font-size

  • 假设设计稿是750px

    • 把整个屏幕划分为15等价,划分标准不一,可以是20份也可以是10等分
    • 每一份作为html字体大小,就是50px
  • 在320px设备的时候,字体大小为320/15=21.33px

    • 用页面的大小除以不同的html字体大小会发现比例还是相同的
  • 以750px为标准设计稿

    • 一个100x100像素的页面元素在750px屏幕下,就是100/50转换为rem是2remx2rem比例是1:1
    • 320px屏幕下,html字体大小为21.33px,则2rem=42.66px,此时宽和高都是42.66px,但是宽和高的比例还是1:1
    • 已经能实现不同屏幕下,页面元素盒子等比例缩放的效果

8.1.3. 设计元素大小取值

  • 页面元素的rem值=页面元素值 /(屏幕宽度/划分的份数)
  • 屏幕宽度/划分的份数就是html的font-size大小
    • 或者:页面元素的rem值=页面元素值/html的font-size大小

8.2. html的font-size

8.2.1. 方案一:媒体查询

  • 可以通过媒体查询来设置不同尺寸范围内的屏幕html的font-size尺寸
  • 缺点
    1. 需要针对不同的屏幕编写大量的媒体查询
    2. 如果动态改变尺寸,不会实时的进行更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@media screen and (min-width:320px){
html {
font-size: 32px;
}
}
@media screen and (min-width:375px){
html {
font-size: 37.5px;
}
}
@media screen and (min-width:414px){
html {
font-size: 41.4px;
}
}

8.2.2. 方案二:编写js代码

  • 如果希望实时改变屏幕尺寸时,font-size也可以实时更改,可以通过js代码
  • 方法
    1. 根据html的宽度计算出font-size的大小,并且设置到html上
    2. 监听页面的实时改变,并且重新设置font-size的大小到html上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1.获取html元素
const htmlEl = document.documentElement;
function setRemUnit(){
// 2. 获取html的宽度(视口宽度)
const htmlWidth = htmlEl.clientWidth;
// 3. 根据宽度计算一个html的font-size的大小
const htmlFontSize = htmlWidth/10;
// 4. 将font-size设置到html上
htmlEl.style.fontSize = htmlFontSize+"px";
}
// 保证第一次进入时,可以设置一次font-size
setRemUnit();
// 当屏幕尺寸发生改变时,实时修改html的font-size
window.addEventListener("resize",function(){
setRemUnit();
});
window.addEventListener("pageshow",function(e){
if(e.persisted){
setRemUnit();
}
});

8.2.3. 方案三:lib-flexible库

  • 手机淘宝团队出的间接高效移动端适配库

  • 不再需要写不同屏幕的媒体查询,js已处理

  • 它的原理是

    • 把当前设备划分为10等份,不同设备下,比例还是一致的
  • https://github.com/amfe/lib-flexible

    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 / 10
    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))

8.3. rem的单位换算

8.3.1. 方案一:手动换算

  • 比如有一个在375px屏幕上,100px宽度和高度的盒子
  • 将100px转成对应的rem值
    • 100/37.5=2.6667rem,其他也是相同的方法计算即可

8.3.2. 方案二:less/scss函数

1
2
3
4
5
6
7
8
@htmlFontSize 37.5px
.pxToRem(@px) {
result: (@px/@htmlFontSize)*1rem;
}
.box {
width: .pxToRem(100)[result];
font-size: .pxToRem(18)[result];
}

8.3.3. 方案三:npm插件

  • postcss-pxtorem

  • 目前在前端的工程化开发中,可以借助于webpack的工具来完成自动的转化

8.3.4. 方案四:VSCode插件cssrem

  • 修改其默认font-size: 16px

  • 打开设置,在搜索设置中直接输入cssroot,把16更改为自己想要的默认HTML字体大小

    效果

8.4. 应用:苏宁易购

9. 适配方案-vw

  • 在flexible GitHub上已经有写过这样的一句话

    由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方。

  • 所以更推荐使用viewport的两个单位vw、wh

  • vw的兼容性

9.1. vw和rem的对比

  • rem事实上是作为一种过渡的方案,利用的也是vw的思想

    • 不管是编写的js,还是flexible的源码
    • 都是将1rem等同于设计稿的1/10,在利用1rem计算相对于整个屏幕的尺寸大小
    • 那么1vw不是刚好等于屏幕的1/100吗?
      • html的font-size就可以写成 10vm
      • 而且不需要动态设置html的font-size值,元素可以直接使用26.6667vm(即2.6667rem*html.font-size的10vm)
  • vw相比于rem的优势

    • 优势一:不需要去计算html的font-size大小,也不需要给html设置这样一个font-size
    • 优势二:不会因为设置html的font-size大小,而必须给body再设置一个font-size,防止继承
    • 优势三:因为不依赖font-size的尺寸,所以不用担心某些原因html的font-size尺寸被篡改,页面尺寸混乱
    • 优势四:vw相比于rem更加语义化,1vw刚才是1/100的viewport的大小
    • 优势五:可以具备rem之前所有的优点
  • vw相比于rem的劣势

    • 不存在极限值,vw始终是相对于视口的变大而变大
    • rem就可以设置媒体查询。如宽度>810px时,font-size固定为一个值
  • vw只面临一个问题,将尺寸换算成vw的单位即可

  • 所以,目前相比于rem,更加推荐大家使用vw(但是理解rem依然很重要)

9.2. vw的单位换算

9.2.1. 方案一:手动换算

  • 比如有一个在375px屏幕上,100px宽度和高度的盒子
  • 将100px转成对应的vw值
    • 100/3.75=26.667,其他也是相同的方法计算即可

9.2.2. 方案二:less/scss函数

1
2
3
4
5
6
7
8
@basic 3.75
.pxToVw(@px) {
result: (@px/@basic)*1vm;
}
.box {
width: .pxToVw(100)[result];
font-size: .pxToVw(18)[result];
}

9.2.3. 方案三:npm插件

  • postcss-px-to-viewport-8-plugin

  • 和rem一样,在前端的工程化开发中,可以借助于webpack的工具来完成自动的转化

9.2.4. 方案四:VSCode插件cssrem

  • 修改其默认设计稿宽度: 750

  • 打开设置,在搜索设置中直接输入cssvw,把750更改为自己想要的

10. 适配方案-flex弹性布局

11. 适配方案-响应式布局

11.1. 布局容器

  • 需要一个父级作为布局容器,来配合子级元素来实现变化效果
  • 在不同屏幕下,通过媒体查询来改变这个布局容器的大小,再改变里面子元素的排列方式和大小,从而实现不同屏幕下,看到不同的页面布局和样式变化

11.2. 尺寸划分

  • 超小屏幕(手机,<768px):设置宽度为100%
  • 小屏幕(平板,≥768px):设置宽度为750px
  • 中等屏幕(桌面显示器,≥992px):设置宽度为970px
  • 大屏幕(大桌面显示器,≥1200px):设置宽度为1170px
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 超小屏幕(手机,<768px):设置宽度为100% */
@media screen and (max-width:767px) {
.container{
width: 100%;
}
}
/* 小屏幕(平板,≥768px):设置宽度为750px */
@media screen and (min-width:768px) {
.container{
width: 750px;
}
}
/* 中等屏幕(桌面显示器,≥992px):设置宽度为970px */
@media screen and (min-width:992px) {
.container{
width: 970px;
}
}
/* 大屏幕(大桌面显示器,≥1200x):设置宽度为1170px */
@media screen and (min-width:1200px) {
.container{
width: 1170px;
}
}

11.3. 案例

  • 屏幕≥768px时,布局容器container宽度为750px

    • container里面包含8个li盒子,每个盒子的宽度为93.75(750/8)px
  • 屏幕缩放,宽度<768px时,宽度修改为100%

    • 此时8个li盒子宽度修改为33.33%

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="container">
<ul>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
</ul>
</div>
</body>
</html>

index.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
ul{
list-style: none;
}
.container{
width: 750px;
height: 44px;
margin: 0 auto;
background-color: aqua;
}

.container ul li{
float: left;
width: 12.5%;
height: 30px;
border: 1px solid purple;
}
/* 超小屏幕(手机,<768px):设置宽度为100% */
@media screen and (max-width:767px) {
.container{
width: 100%;
}
.container ul li{
width: 33.33%;
}
}

12. 触屏事件touch

触屏事件 说明
touchstart 手指触摸到一个DOM元素时触发
touchmove 手指在一个DOM元素上滑动时触发
touchend 手指从一个DOM元素上移开时触发

12.1. 触摸事件

触摸列表 说明
touches 正在触摸屏幕的所有手指的一个列表
targetTouches 正在触摸当前DOM元素上的手指的一个列表
changedTouches 手指状态发生了改变的列表,从无到有,从有到无变化

12.2. 移动端拖动元素

⚠️ Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive.

解决

div.addEventListener("监听的事件",function,{passive:false});

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
<!DOCTYPE html>
<html lang="en">
<head>
<style>
div{
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div></div>
<script>
var div = document.querySelector("div");
//手指原始坐标
var startX;
var startY;
//盒子原来的距离
var x;
var y;
div.addEventListener("touchstart",function(e){
// console.log(e);
startX = e.targetTouches[0].pageX;
startY = e.targetTouches[0].pageY;

x = this.offsetWidth;
y = this.offsetHeight;

});
div.addEventListener("touchmove",function(e){
//阻止屏幕滚动默认行为
e.preventDefault();
//手指移动距离=手指移动之后的距离-手指初始坐标
var moveX = e.targetTouches[0].pageX - startX;
var moveY = e.targetTouches[0].pageY - startY;
//移动盒子 = 盒子原来的位置+手指移动的距离
this.style.left = x +moveX+'px';
this.style.top = y + moveY+'px';

});
</script>
</body>
</html>

13. 轮播图

13.1. 代码

13.1.1. html

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
<!-- 轮播图 -->
<div class="focus">
<ul>
<li>
<a href=""><img src="uploads/back4.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="uploads/back1.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="uploads/back2.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="uploads/back3.png" alt=""></a>
</li>
<li>
<a href=""><img src="uploads/back4.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="uploads/back1.jpg" alt=""></a>
</li>
</ul>
<ol>
<li class="active"></li>
<li></li>
<li></li>
<li></li>
</ol>

</div>

13.1.2. 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* 轮播图 */
.focus {
position: relative;
width: 100%;
height: 129px;
max-width: 540px;
min-width: 320px;
overflow: hidden;
margin-top: 55px;
}

.focus ul {
margin-left: -100%;
width: 600%;
}


.focus li {
float: left;
width: 16.66%;
}

.focus li a img {
width: 100%;
height: 129px;
}

.focus ol{
position: absolute;
bottom: 5px;
right: 5px;
list-style: none;
}
.focus ol li{
float: left;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #fff;
margin: 0 5px;
transition: .2s;
}
.focus ol li.active{
width: 20px;
border-radius: 5px;
}

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
window.addEventListener("load",function(){
var focus = document.querySelector(".focus");
var ul = focus.querySelector("ul");
var ol = focus.querySelector("ol");
var width = focus.offsetWidth;
var num = 0;
function move(){
num++;
ul.style.transition='all .5s';
ul.style.transform = 'translateX('+-num*width+'px)';

}
var timer = setInterval(move,2000);

//监听过渡事件
ul.addEventListener("transitionend",function(){
if(num==ul.children.length-2){
num=0;
ul.style.transition='none';
ul.style.transform = 'translateX(0px)';
}
else if(num<0){
num=ul.children.length-3;
ul.style.transition='none';
ul.style.transform = 'translateX('+-num*width+'px)';
}

//小圆点
//选出ol中所有带有active类的元素,去掉类名active
ol.querySelector(".active").classList.remove("active");
ol.children[num].classList.add("active");
});

//手指拖动
var startX = 0;
var flag = false;
ul.addEventListener("touchstart",function(e){
clearInterval(timer);
startX = e.targetTouches[0].pageX;
},{passive:false});
var moveX=0;
ul.addEventListener("touchmove",function(e){

moveX = e.targetTouches[0].pageX - startX;
//移动距离=盒子原来的距离+手指移动距离
var translatex = -num*width + moveX;
ul.style.transition='none';
ul.style.transform = 'translateX('+translatex+'px)';
flag = true;
e.preventDefault();
},{passive:false});
ul.addEventListener("touchend",function(e){
if(flag){
if(Math.abs(moveX)>100){
if(moveX>0){
num--;

}else{
num++;
}

}
ul.style.transition='all .5s';
ul.style.transform = 'translateX('+-num*width+'px)';
timer = setInterval(move,2000);
}

},{passive:false});
});

13.2. 移动端插件

13.2.1. swiper插件

https://www.swiper.com.cn/

13.2.2. superslide

http://www.superslide2.com/

13.2.3. iscroll

https://github.com/cubiq/iscroll

13.2.4. zy.media.js

zyMedia是掌阅科技开源的一款基于HTML5的、轻量级的、实现移动端全平台统一UI的多媒体播放器,该播放器已经在千万级设备上适配使用

https://github.com/ireaderlab/zyMedia

https://gitcode.net/mirrors/ireaderlab/zyMedia

14. click延时解决方案

移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放页面

  1. 禁止缩放

    <meta name="viewport" content="user-scalable=no">

  2. 利用touch事件自己封装解决

    • 当手指触摸屏幕,记录当前触摸时间

    • 手指离开屏幕,离开的时间减去触摸时间

    • 如果事件小于150ms,并且没有滑过屏幕,定义为点击

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      function tap(obj,callback){
      var isMove = false;
      var startTime = 0;
      obj.addEventListener("touchstart",function(e){
      //记录触摸时间
      startTime = Date.now();
      });
      obj.addEventListener("touchmove",function(e){
      //是否有滑动,有滑动算拖拽,不算点击
      isMove=true;
      });
      obj.addEventListener("touchend",function(e){
      //手指触摸和离开时间小于150ms算点击
      if(!isMove && (Date.now()-startTime<150)){
      callback&&callback();
      }
      //重置
      isMove=false;
      startTime=0;
      });
      }
      //调用
      tap(div,function(){});
  3. 插件 fastclick.js

    https://github.com/ftlabs/fastclick

    https://www.aliyundrive.com/s/XSQzVFYaCYu

    Include fastclick.js in your JavaScript bundle or add it to your HTML page like this:

    1
    <script type='application/javascript' src='/path/to/fastclick.js'></script>

    The script must be loaded prior to instantiating FastClick on any element of the page.

    To instantiate FastClick on the body, which is the recommended method of use:

    1
    2
    3
    4
    5
    if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
    FastClick.attach(document.body);
    }, false);
    }

    Or, if you’re using jQuery:

    1
    2
    3
    $(function() {
    FastClick.attach(document.body);
    });
本文结束  感谢您的阅读