移动端开发基础知识

移动端开发

相关概念

​ 在学习移动端之前,我们先来学习一些基础的概念和专有名词,这些知识会在以后的面试、工作沟通中经常用到。

屏幕相关

1.屏幕大小

​ 屏幕大小是指屏幕的对角线长度,单位一般是英寸(inch)。常见的手机屏幕大小有: 3.5寸、4寸、4.7寸、5.0寸、5.5寸、6.0寸等等,英寸与厘米的换算关系为 :1英寸(inch) = 2.54厘米(cm)

2.屏幕分辨率

​ 屏幕分辨率是指屏幕在:横向、纵向上所拥有的物理像素点总数。一般表示用 n * m 表示。

​ 例如: IPhone 6 的屏幕分辨率为 750 * 1334

  • ​ 注意点:
    • 屏幕分辨率是一个固定值,屏幕生产出来就确定了,无法修改!!
    • 屏幕分辨率、显示分辨率是两个概念,电脑或手机的设置中可以修改的是:显示分辨率。

显示分辨率

  • 显示分辨率指的是设备当前所用到的物理像素点数,或者说:屏幕分辨率 >= 显示分辨率。

常见手机分辨率

型号分辨率(物理像素点总和)
IPhone 3G / 3GS320 * 480
IPhone 4 / 4s640 * 960
IPhone 5 / 5s640 * 1136
IPhone 6 / 7 / 8750 * 1334
IPhone 6p / 7p / 8p1242 x 2208
IPhone X1125 * 2436
华为 P301080 * 2340

3.屏幕密度

​ 又称:屏幕像素密度,是指屏幕上每英寸里包含的物理像素点个数,单位是 ppi (pixels per inch),其实这里还有另一个单位 dpi(dots per inch),两个值的计算方式都一样,只是使用的场景不同。 ppi主要用来衡量屏幕,dpi 用来衡量打印机等。

像素相关

1.物理像素

物理像素又名:设备像素,物理像素是一个长度单位,单位是px,1个物理像素就是屏幕上的一个物理成像点,所谓物理成像点,就是屏幕中一个微小的发光物理元器件(“可简单理解为超级微小的灯泡”),是屏幕能显示的最小粒度。屏幕的物理像素点数(分辨率)是手机屏幕的一个重要参数,由屏幕制造商决定,屏幕生产后无法修改。例如 iPhone 6 的横向上拥有的物理像素为750、纵向上拥有的物理像素为1334 ,我们也可以用:750* 1334表示。

物理像素图示:

2. css 像素

css像素又名: 逻辑像素,css像素是一个抽象的长度单位,单位也是px,它是为 Web 开发者创造的,用来精确的度量Web 页面上的内容大小。我们在编写css、JS中所使用的都是css像素(可以理解为:“程序员像素”);

思考:我代码中所写的1px(css像素 ),到了屏幕上到底对应几个物理像素呢?是1个css像素就对应1个物理像素(“发光的灯泡”)吗?要探讨这个对应关系,就要学习接下来的新概念:设备独立像素。

3.设备独立像素

设备独立像素简称 DIP 或 DP(device-independent pixel),又称:屏幕密度无关像素。

引言: 在没出现【高清屏】的年代,1个css像素对应1个物理像素,但自从【高清屏】问世,二者就不再是1对1的关系了。苹果公司在2010年推出了一种新的显示标准:在屏幕尺寸不变的前提下,把更多的物理像素点压缩至一块屏幕里,这样分辨率就会更高,显示效果就会更佳细腻。苹果将这种屏幕称为:Retina 屏幕(又名:视网膜屏幕),与此同时推出了配备这种屏幕的划时代数码产品——iPhone4。

我们来看一个场景:

​ 程序员写了:width = 2px,height = 1px 的盒子,若1个css像素直接对应1个物理像素,由于iPhone3G/S 与iPhone4屏幕尺寸相同,但iPhone4的屏幕能容纳下更多的物理像素点,所以iPhone4的物理像素点比iPhone3G/S小很多,那么这个盒子在iPhone4屏幕上也就会比iPhone3G/S屏幕上小很多,而事实是iPhone3G/S 和 iPhone4下这个盒子是一样大的!!!,只不过 iPhone4更加细腻、清晰。如何做到的呢?这就要靠设备独立像素。

对比:iPhone3G/S 与 iPhone4的成像效果:

​ 设备独立像素可以理解:我们的电脑、手机设备中有一个坐标系,这个坐标系中的一个点,就是一个设备独立像素,对应一个可以由程序去使用的虚拟像素(css像素)。程序根据屏幕特性、将设备独立像素上的点对应到屏幕上的物理像素点。

设备独立像素的出现,使得即使在【高清屏】下,(例如苹果的Retina屏),也可以让元素有正常的尺寸,让代码不受到设备的影响,它是设备厂商根据屏幕特性设置的,无法更改。

与物理像素关系

  • 普通屏幕下 1 个设备独立像素 对应 1 个物理像素
  • 高清屏幕下 1 个设备独立像素 对应 N 个物理像素

与css像素关系

  • 在无缩放的情况下(标准情况):1css像素 = 1设备独立像素

4.像素比

​ 像素比(dpr):单一方向上设备【物理像素】和【设备独立像素】的比例。即:dpr = 物理像素 / 设备独立像素
物理像素就是最小的那个单位

window.devicePixelRatio

几款手机的屏幕像素参数,点击这里查看更多

型号分辨率(物理像素点总和)设备独立像素(dip或dp)像素比(dpr)
IPhone 3GS320 * 480320 * 4801
IPhone 4 / 4s640 * 960320 * 4802
IPhone 5 / 5s640 * 1136320 * 5682
IPhone 6 / 7 / 8750 * 1334375 * 6672
IPhone 6p / 7p / 8p1242 x 2208414 * 7363
IPhone X1125 * 2436375 * 8123
HUAWEI P101080 x 1920360 x 6403

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个的像素点组成的。放大后会失真。(常见有:png、jpeg、jpg、gif)

    一般使用:PhotoShop等软件进行编辑

矢量图

  • 又称为面向对象图像或绘图图像,在数学上定义为一系列由线连接的点,放大后不会失真。(常见:svg)

    一般使用:Adobe Illustrator,Sketch等软件进行编辑

位图像素也是一个长度单位,位图像素可以理解为位图中的一个“小格子”,是位图的最小单元。

根据设备显示图片

注意:1个位图像素对应1个物理像素,图片才能得到完美清晰的展示。

具体编码时借助媒体查询:@media screen and (-webkit-min-device-pixel-ratio:x)

 #demo {
        width: 200px;
        height: 200px;
        background-size: 100%;
        background-image: url('./imgs/logo.png');
      }

 @media screen and (-webkit-min-device-pixel-ratio: 2) {
        #demo {
          background-image: url('./imgs/logo@2x.png');
        }
}

视口相关

pc端视口

​ - 在pc端,视口的默认宽度和浏览器窗口的宽度一致。在 css 标准文档中,视口也被称为:初始包含块,它是所有 css 百分比宽度推算的根源,在pc端可通过如下几种方式获取宽度:

  • 注意: 一个盒子想要占满宽度,应该和视口的宽度是一致的
console.log('浏览器最干净的可视区域',document.documentElement.clientWidth) 
//浏览器最干净的可视区域(常用)


console.log('包含滚动条可视区域',window.innerWidth);
//浏览器包含滚动条可视区域


console.log('包含滚动条、边框的可视区域',window.outerWidth);
//浏览器包含滚动条、边框的可视区域


console.log('与浏览器无关,设备整个屏幕的宽度',screen.width);
//与浏览器无关,设备整个屏幕的宽度

移动端视口

​ 在移动端,浏览器厂商面临着一个比较大的问题,他们如何将数以万计甚至可以说是数以亿计的pc端网页完整的呈现在移动端设备上,并且不会出现横向滚动条呢?那就要引出移动端的三个概念:1.布局视口、2.视觉视口、3. 理想视口

1.布局视口

  • 移动端想要占满屏幕,布局视口有多宽,就应该写多宽
  • 100%是980px ipad pro是1024px
 console.log('移动端布局视口的宽度',document.documentElement.clientWidth)

​ 用于解决早期的页面在手机上显示的问题,早期的时候我们这样做:pc端网页宽度一般都为:960px ~ 1024px 这个范围,就算超出了该范围,960px ~ 1024px这个区域也依然是版心的位置,浏览器厂商针对移动端设备设计了一个容器,先用这个容器去承装pc端的网页,这容器的宽度一般是**980px左右,不同的设备可能有所差异,但相差并不大;随后将这个容器等比例压缩**到与手机等宽,这样就可以保证没有滚动条且能完整呈现页面,但是这样做依然有问题:网页内容被压缩的太小,严重影响用户体验。

移动端获取布局视口方式:document.documentElement.clientWidth

注意:布局视口经过压缩后,横向的宽度用css像素表达就不再是375px了,而是980px,因为布局视口是被压缩,而不是截取。

2.视觉视口

  • 视觉视口打印出来也是980px,视觉视口不影响布局
获取方式:window.innerWidth
不过在Android2、Opera mini 、UC8 中无法正确获取。(一般不通过代码看视觉视口)

​ 视觉视口就是用户可见的区域,它的绝对宽度永远和设备屏幕一样宽,但是这个宽度里所包含的css像素值是变化的,例如:一般手机会将980个css像素放入视觉视口中,而ipad Pro会将1024个css像素放入视觉视口中,而且也和用户的缩放相关。

描述一下你的屏幕:

现在以iPhone6为例,我们描述一下屏幕(横向上):

  1. 物理像素:750px
  2. 设备独立像素:375px
  3. css像素:980px

3.理想视口

  <!-- 这行代码一写, 在移动端就开启的理想视口 -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

与屏幕(设备独立像素)等宽的布局视口,称之为理想视口,所以也可以说理想视口是一种标准:让布局视口宽度 与 屏幕等宽(设备独立像素),靠meta标签实现。

理想视口的特点:

  • 布局视口和屏幕等宽,以iPhone6为例,符合理想视口标准之后:布局视口宽度:375px,设备独立像素:375px。
  • 用户不需要缩放、滚动就能看到网站的全部内容。
  • 要为移动端设备单独设计一个移动端网站。

设置理想视口的具体方法:

<meta name="viewport" content="width=device-width,initial-scale=1.0" />

总结

不写meta标签(不符合理想视口标准):

  1. 描述屏幕:物理像素:750px 、设备独立像素:375px、css像素:980px。
  2. 优点:元素在不同设备上,呈现效果几乎一样,因都是通过布局容器等比缩放的,例如200宽的盒子:200/980
  3. 缺点:元素太小,页面文字不清楚,用户体验不好。

meta标签(符合理想视口标准):

  1. 描述屏幕:物理像素:750px 、设备独立像素:375px、css像素:375px。

  2. 优点:

    1. 页面清晰展现,内容不再小到难以观察,用户体验较好。

    2. 更清晰的像素关系:布局视口 = 视觉视口 = 设备独立像素 = 375px。

    3. 更清晰的dpr,即dpr = 物理像素/设备独立像素 = 物理像素/css像素。

      例如:dpr=2的设备,1 * 1 css像素 = 1 * 1 设备独立像素 = 2 * 2 物理像素

  3. 缺点:同一个元素,在不同屏幕(设备)上,呈现效果不一样,例如200宽的盒子: 375/375 和 375/414 (不是等比显示)

  4. 解决缺点:做适配。

缩放

PC 端

放大时

  • 视口变小
  • 元素的 css 像素值不变,但一个css像素所占面积变大了。

缩小时

  • 视口变大
  • 元素的 css 像素值不变,但一个css像素所占面积变小了。

移动端

放大时

  • 布局视口不变
  • 视觉视口变小

缩小时

  • 布局视口不变
  • 视觉视口变大

注意:移动端缩放不会影响页面布局,因为缩放的时候,布局视口视口没有变化,简记:移动端的缩放没有改变布局视口的任何东西!

viewport

meta-viewport 标签是苹果公司在 2007 年引进的,用于移动端布局视口的控制。

使用示例:

<meta name="viewport" content="width=device-width,initial-scale=1.0">

完整写法: 
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">

viewport 相关选项

 initial-scale的值 是 当前设备独立像素宽度 / 布局视口 计算出来

 没有meta标签  980px

 // 独立像素
 <meta name="viewport" content="width=device-width">

 // 独立像素
 <meta name="viewport" content="width=device-width, initial-scale=1.0">

 1. width属性的值是多少,就表示当前设备的布局视口是多大
 1.1 值 device-width 将设备独立像素的宽度设置给布局视口
 1.2 值 具体的数据,但是不建议   安卓手机不支持

 2. initial-scale: 值是一个数字. 也是间接的控制了布局视口的宽度 
 公式: 当前设备独立像素宽度 / 布局视口 ==> 值
 注意: width和initial-scale做的事情是一样的.这样写的目的是为了加双保险,保证当前设备的布局视口一定是当前设备独立像素的宽度

  1. width 布局视口的宽度
  2. initial-scale 初始缩放比例
  3. maximum-scale 最大缩放比例
  4. minimum-scale 最小缩放比例
  5. user-scalable 是否允许用户缩放
  6. viewport-fit 设置为cover值可以解决刘海屏的留白问题
2. width

width值可以是设备宽度标识 device-width,也可以是具体值,但有些安卓手机是不支持具体值,IOS全系列都支持。

3. initial-scale
  1. initial-scale 为页面初始化时的显示比例。

  2. scale = 屏幕宽度(设备独立像素) / 布局视口宽度。

  3. 只写initial-scale = 1.0 也可以实现完美视口,但为了良好的兼容性,width=device-width, initial-scale=1.0一般一起写。

    注意: 当width和initial-scale的值冲突时, 谁的值大,就以谁为主

4. maximum-scale
  1. 设置允许用户最大缩放比例,苹果浏览器 safari 不认识该属性

  2. maximum-scale =屏幕横向独立像素值(设备独立像素) / 视觉视口宽度值

5. minimum-scale
  1. 设置允许用户最小缩放比例。

  2. minimum-scale = 屏幕横向独立像素值(设备独立像素) / 视觉视口宽度值

6. user-scalable

​ 是否允许用户通过手指缩放页面。苹果浏览器 safari 不认识该属性

7.viewport-fit

​ 设置为 cover 可以解决『刘海屏』的留白问题

适配

一、为什么要做适配?
由于移动端设备的屏幕尺寸大小不一,会出现:同一个元素,在两个不同的手机上显示效果不一样(比例不同)。要想让同一个元素在不同设备上,显示效果一样,就需要适配,无论采用何种适配方式,中心原则永远是:等比!。

主流的适配方式有三种:

  • viewport 适配
  • rem 适配(主流方式,几乎完美适配)
  • vw、vh适配

1.viewport 适配

  • 方法:拿到设计稿之后,设置布局视口宽度为设计稿宽度,然后直接按照设计稿给宽高进行布局即可。
  • 优点:不用复杂的计算,直接使用图稿上标注的px值
  • 缺点:
    • 有些不希望被适配的东西,例如边框,也被适配了,导致比设计稿大的设备上边框太粗。
    • 图片失真
    • 不能使用完整的meta标签,会导致在某些安卓手机上有兼容性问题。
  • 一般适用于:计图稿宽度 < 375
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- 这个等于写死了布局视口  如果有后面会选择大的,而后面的会根据浏览器变化 -->
    <meta name="viewport" content="width=375">
    <!-- 如果写两个值,那个值大用哪个   initial是独立像素/ ? =1   ?就是布局视口 -->
    <!-- 布局视口哪个大就用哪个    后面的会根据视口一直变化 -->
    <!-- <meta name="viewport" content="width=375 initial-scale=1.0"> -->
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 345px;
            height: 150px;
            background-color: red;
            margin: 15px;
        }
    </style>
</head>
<body>
    <div class="box"></div>
    <script>
        console.log(document.documentElement.clientWidth)
    </script>
</html>

2.rem适配

em 和 rem介绍

em 和 rem 都是 css 中的长度单位。而且两个都是相对长度单位,不过两个有点区别

  • em 相对的是父级元素的字体大小
  • rem 相对的是根元素的字体大小

rem适配的原理:编写样式时统一使用rem为单位,在不同设备上动态调整根字体大小

<style>
        html{
            font-size: 20px;
        }
        body{
            font-size: 10px;
        }
        .box{
            width: 10em;
            /* 100px */
            height: 10rem;
            /* 200px */
            background-color: green;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>

em: 是一个相对单位. 相对谁: 相对的是父元素的font-size的值
最终在渲染的时候,单位还是px.
如何计算: em的值 乘以 父元素的font-size

        rem: 是一个相对单位. 相对谁: 相对根元素的font-size的值
        根元素是html 最终渲染的单位也是px
        如何计算: rem的值 乘以 根元素的font-size

方案一:淘宝/百度

淘宝、百度的移动端页面用的此方案

  1. 设置完美视口
  2. 通过js设置根字体大小 = *( 当前设备横向独立像素值 100) / 设计稿宽度
  3. 编写样式时,直接以rem为单位,值为:设计值 / 100
  4. 增加 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;
      }

      .box {
        /* width: 345px;
        height: 150px;
        background-color: green;
        margin: 15px; */
        
        width: 3.45rem;
        height: 1.50rem;
        background-color: green;
        margin: 0.15rem;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>

    <script>
      // 1.动态设置根字体的大小  根字体 = (设备横向独立像素 * 100) / 设计稿的宽度
      // 2.写样式的时候,写rem单位,值是设计稿的值/100
      
      function adapter() {
        const rootFontSize = document.documentElement.clientWidth * 100 / 375
        document.documentElement.style.fontSize = rootFontSize + "px"
      }

      adapter()

      // 视口发生变化的时候触发
      window.onresize = adapter

    </script>
  </body>
</html>

方案二/搜狐/唯品会

搜狐、唯品会的移动端页面用的此方案

  1. 设置完美视口
  2. 通过js设置根字体大小 = 当前设备横向独立像素值 / 10
  3. 编写样式时,直接以rem为单位,值为:设计值 / (设计稿宽度 / 10)
  4. 增加 JS 代码进行实时适配
  • html
<!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;
      }
    </style>
    <link rel="stylesheet" href="./06-index.css" />
  </head>
  <body>
    <div class="box"></div>

    <script>
      /* 
        1.设置根字体大小= 设备独立像素宽度/10
        2.写样式的时候,用设计稿的值/根字体的大小 单位是rem
      */
      function adapter() {
        const rootFontSize = document.documentElement.clientWidth / 10;
        document.documentElement.style.fontSize = rootFontSize + "px";
      }
      adapter();

      window.onresize = adapter;
    </script>
  </body>
</html>

  • less
@font: 37.5rem;
.box {
  width: 345 / @font;
  height: 150 / @font;
  background-color: red;
  margin: 15 / @font;
}

3.vw适配

vw和vh是两个相对单位

  • 1vw = 等于布局视口宽度的1%
  • 1vh = 等于布局视口高度的1%

不过vw和vh有一定的兼容性问题:详见:这里

<!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;
      }

      /* 
        1vw = 等于布局视口宽度的1%
        1vh = 等于布局视口高度的1%
        */
      .box {
        width: 92vw;
        height: 40vw;
        background-color: red;
        margin: 4vw;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
  </body>
</html>

4.1px物理像素边框

高清屏幕下 1px 对应更多的物理像素,所以 1 像素边框看起来比较粗,解决方法如下

方法一 @media

使用媒查询:

@media screen and (-webkit-min-device-pixel-ratio:2){
    #demo{
        border: 0.5px solid black;
    }
}
  • 完整版
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
    <meta name="viewport" content="width=375" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .box {
        width: 100px;
        height: 100px;
        background-color: red;
        border: 1px solid black;
        margin: 30px;
      }

      .box1 {
        width: 100px;
        height: 100px;
        background-color: red;
        border: 1px solid black;
        margin: 30px;
      }

      @media screen and (-webkit-min-device-pixel-ratio: 2) {
        .box1 {
          border: 0.5px solid black;
        }
      }

      @media screen and(-webkit-min-device-pixel-ratio:3) {
        .box1 {
          border: 0.3333px solid black;
        }
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
    <div class="box1"></div>
  </body>
</html>

方法二

​ 根据dpr扩大布局视口,例如dpr为n则布局视口改为原来的n倍,则元素尺寸均变为原来的n分之一,为了保证元素尺寸比例不变,扩大根字体为原来的n倍,但整个过程中边框一直用px作为单位,不用rem。

  1. rem 页面布局
function mobile (){
   document.documentElement.style.fontSize = (document.documentElement.clientWidth * 100)/375 + 'px'
}
mobile()
window.onresize = mobile
  1. 元素的边框设置为 1px

  2. 通过 viewport 中的 initial-scale 将布局视口扩大n倍,这样页面元素就比原来缩小了n倍

var viewport = document.querySelector('meta[name=viewport]')
var scale = 1 / window.devicePixelRatio
viewport.setAttribute('content', 'width=device-width,initial-scale=' + scale);
  1. 重新设置根元素字体
var fontSize = parseInt(document.documentElement.style.fontSize);
document.documentElement.style.fontSize = fontSize * window.devicePixelRatio + 'px'
  • 完整版
<!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;
      }
      .box {
        width: 3.45rem;
        height: 1.5rem;
        background-color: red;
        margin: 0.15rem;
        box-sizing: border-box;
        border: 1px solid black;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
    <script>
      /*
          1.动态设置根字体的大小
          2.写样式的时候,写rem单位,值是由设计稿的值/100
        */

      function adapter() {
        // 设置根字体的大小 = (设备横向独立像素值 * 100) / 设计稿宽度
        const rootFontSize = (document.documentElement.clientWidth * 100) / 375;
        document.documentElement.style.fontSize = rootFontSize + "px";
      }
      adapter();
      window.onresize = adapter;

      // 动态的修改布局视口
      // 让布局视口变大,对应的盒子就变小了
      const viewport = document.querySelector("meta[name=viewport]");
      const scale = 1 / window.devicePixelRatio;
      viewport.setAttribute(
        "content",
        "width=device-width, initial-scale=" + scale
      );
      // 盒子要恢复到原来的样子,所以让根标签的字体变大
      const fontSize = parseInt(document.documentElement.style.fontSize);
      document.documentElement.style.fontSize =
        fontSize * window.devicePixelRatio + "px";
      // 任何需要处理1px边框的代码,直接写死一个px就可以了
    </script>
  </body>
</html>

移动端事件

事件类型

移动端事件列表

touchstart

  • 元素上触摸开始时触发
  • touchstart 事件可用于元素触摸的交互,比如页面跳转,标签页切换

touchmove

  • 元素上触摸移动时触发
  • touchmove 事件可用于页面的滑动特效,网页游戏,画板

touchend

  • 手指从元素上离开时触发
  • touchend 事件主要跟 touchmove 事件结合使用

touchcancel

触摸被打断时触发
touchcancel 使用率不高

这几个事件最早出现于IOS safari中,为了向开发人员转达一些特殊的信息。

注意:

  • touchmove 事件触发后,即使手指离开了元素,touchmove 事件也会持续触发
  • 触发 touchmove 与 touchend 事件,一定要先触发 touchstart
  • 事件的作用在于实现移动端的界面交互

示例

<!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>
        #box{
            width: 100px;
            height: 100px;
            background-color: red;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <!-- 这个方式只用于开发中,不用再获取元素了,直接给元素id绑定事件 -->
    <script>
        box.ontouchstart = function(){
            console.log("手指触摸到屏幕了")
        }

        box.addEventListener("touchmove",function(){
            console.log("touchmove 手指在屏幕上移动 ")
        })

        box.addEventListener("touchend",function(){
            console.log("手指离开了屏幕")
        })

        
    </script>
</body>
</html>

在这里插入图片描述

点击事件

事件绑定

方式一

box.ontouchstart = function(){
    console.log('touch start')
}

方式二

box.addEventListener('touchstart', function(){
	console.log('touch start')
})

点击穿透

touch 事件结束后会默认触发元素的 click 事件,如没有设置完美视口,则事件触发的时间间隔为 300ms 左右,如设置完美视口则时间间隔为 30ms 左右(备注:具体的时间也看设备的特性)。

​ 如果 touch 事件隐藏了元素,则 click 动作将作用到新的元素上,触发新元素的 click 事件或页面跳转,此现象称为点击穿透

非完美视口触发click

<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->

<body>
    <div id="box"></div>
    <!-- 这个方式只用于开发中,不用再获取元素了,直接给元素id绑定事件 -->
    <script>
      let startTime = 0;
      box.ontouchstart = function () {
        console.log("手指触摸到屏幕了");
        startTime = Date.now()
      };

      box.addEventListener("touchmove", function () {
        console.log("touchmove 手指在屏幕上移动 ");
      });

      box.addEventListener("touchend", function () {
        console.log("手指离开了屏幕");
      });

      box.onclick = function () {
        console.log("click事件触发了");

        let endTime = Date.now();
        console.log(endTime - startTime);
      };

   </script>
</body>

在这里插入图片描述

完美视口触发click

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<script>
      let startTime = 0;
      box.ontouchstart = function () {
        console.log("手指触摸到屏幕了");
        startTime = Date.now()
      };

      box.addEventListener("touchmove", function () {
        console.log("touchmove 手指在屏幕上移动 ");
      });

      box.addEventListener("touchend", function () {
        console.log("手指离开了屏幕");
      });

      box.onclick = function () {
        console.log("click事件触发了");

        let endTime = Date.now();
        console.log(endTime - startTime);
      };

    </script>

在这里插入图片描述

点击穿透案例

<!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;
      }
      html,
      body,
      #app {
        width: 100%;
        height: 100%;
      }
      #app {
        position: relative;
      }
      #banner {
        width: 100%;
        height: 200px;
        background-color: orange;
      }
      #shade {
        position: absolute;
        text-align: center;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(1, 1, 1, 0.5);
      }
      h1 {
        margin-top: 100px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <a href="https://www.baidu.com">
        <div id="banner"></div>
      </a>
      <div id="shade">
        <h1>恭喜,一等奖</h1>
        <button id="btn">点击关闭</button>
      </div>
    </div>
    <script>
      const btn = document.getElementById("btn");
      const shade = document.getElementById("shade");
      btn.addEventListener("touchstart", () => {
        shade.style.display = "none";
      });
    </script>
  </body>
</html>

  • 点击以后会跳转到百度,因为触发了onclick
    在这里插入图片描述

1.event.preventDefault()

阻止默认行为

<script>
  const btn = document.getElementById("btn");
  const shade = document.getElementById("shade");
  btn.addEventListener("touchstart", (event) => {
    // 方式一,阻值默认事件
    event.preventDefault();
    shade.style.display = "none";
  });
</script>

2.使背后元素不具有click属性

  • 一定要注释掉之前的a标签
    使背后元素不具备click特性,用touchXxxx代替click,但是长按也会触发 最好用第三种
  • a标签的灵魂就是跳转,也有利于seo优化,所以不建议使用这种方法

移动端页面跳转可以使用 a 链接,也可以使用 touchstart 事件来触发 JS 代码完成跳转

  • 效率上,touchstart 速度更快
  • SEO 优化上, a 链接效果更好
 <body>
  <div id="app">
	//注释掉之前的a标签
    <!-- <a href="https://www.baidu.com">
      <div id="banner"></div>
    </a> -->
   
    <a>
      <div id="banner"></div>
    </a>
    <div id="shade">
      <h1>恭喜,一等奖</h1>
      <button id="btn">点击关闭</button>
    </div>
  </div>
  <script>
    const btn = document.getElementById("btn");
    const shade = document.getElementById("shade");
    btn.addEventListener("touchstart", (event) => {
      shade.style.display = "none";
    });

    // 方式二:使其背后的元素不具备click特性,用touchstart代替,记得要注释掉之前的a标签
    const link = document.getElementsByTagName("a")[0];
    link.ontouchstart = function () {
      console.log(123);
      window.location.href = "https://www.baidu.com";

    };
  </script>
</body>

3.背后元素怒暂时失去click事件

让背后的元素暂时失去click事件,300毫秒左右再复原,但是我们一般不设置300,因为有误差 可以400+

/* 这个属性让a标签失去click事件 */
      a {
        pointer-events: none;
      }

  </style>
</head>
<body>
  <div id="app">
    <a href="https://www.baidu.com">
      <div id="banner"></div>
    </a>
    <div id="shade">
      <h1>恭喜,一等奖</h1>
      <button id="btn">点击关闭</button>
    </div>
  </div>
  <script>
    const btn = document.getElementById("btn");
    const shade = document.getElementById("shade");
    const link = document.getElementsByTagName("a")[0];

    btn.addEventListener("touchstart", (event) => {
      shade.style.display = "none";

      // 300毫秒左右,恢复a标签的click事件
      setTimeout(() => {
        link.style.pointerEvents = "auto";
      }, 400);
    });
  </script>
</body>

4.隐藏元素延时执行

让隐藏的元素延迟300毫秒左右再隐藏

  <body>
    <div id="app">
      <a href="https://www.baidu.com">
        <div id="banner"></div>
      </a>
      <div id="shade">
        <h1>恭喜,一等奖</h1>
        <button id="btn">点击关闭</button>
      </div>
    </div>
    <script>
      const btn = document.getElementById("btn");
      const shade = document.getElementById("shade");
      const link = document.getElementsByTagName("a")[0];

      btn.addEventListener("touchstart", (event) => {
        // 第四种方式
        setTimeout(() => {
          shade.style.display = "none";
        }, 400);
      });
    </script>
  </body>

关于页面跳转的选择

移动端页面跳转可以使用 a 链接,也可以使用 touchstart 事件来触发 JS 代码完成跳转

  • 效率上,touchstart 速度更快
  • SEO 优化上, a 链接效果更好

事件对象属性

    <style>
      #box {
        width: 300px;
        height: 300px;
        background-color: pink;
      }
    </style>
  </head>
  <body>
    <div id="box"></div>
    <script>
      box.ontouchstart = function (event) {
        console.log(event);
      };
    </script>
  </body>

在这里插入图片描述

touch 事件对象中有 3 个非常重要的属性

changedTouches

 box.ontouchend = function (e) {
        console.log(e);
      };

离开 改变的触点,从无到有,从有到无都属于改变
在这里插入图片描述

targetTouches

元素上的触点

touches

屏幕上的触点

触点对象

每一个触点对象都包含一些位置信息,其中包括

clientX

相对可视区域左侧的偏移

clientY

相对可视区域顶侧的偏移

pageX

相对文档左侧的偏移

pageY

相对文档顶部的偏移

附录

1. 真机测试

真机测试是项目测试必需的一个流程,一定要掌握!!!

方法一:内网穿透

​ 第一步:vscode打开一个工程,找到页面,右键==>Open with live server

​ 第二步:下载utools工具箱,打开器内部的内网穿透插件,输入好:外网域名、内网地址、内网端口,点击确定。

​ 第三步:打开网页,替换掉地址栏里面的http://127.0.0.1:5500/为 http://xxx.xxxx.xx:5500/

​ 第四步:借助chrome插件“极速二维码”生成当前页面二维码,手机扫描即可。

​ 优势:任何有网络的地方都可以访问你的项目。

​ 劣势:速度慢、若有大量图片等资源会卡顿。

方法二:共处同一局域网

​ 第一步:关闭防火墙,chrome中下载插件:“极速二维码”

​ 第二步:用vscode打开项目文件夹,注意必须是文件夹,随后右键==>Open with live server

​ 第三步:cmd窗口==>ipconfig命令,记下自己的ip地址==>替换掉地址栏里面的http://127.0.0.1:5500/为http://xxx.xxxx.xxx.xx:5500/

​ 第四步:使用wifi共享软件开启一个wifi,手机连接上该wifi

​ 第五步:借助chrome插件“极速二维码”生成当前页面二维码,手机扫描即可。

​ 优势:速度快,即便网页中资源很多,也不卡顿。

​ 劣势:只有共处一个网段内(连接上你开启的wifi)的设备才能查看。

补充

1.getComputedStyle

  • Js中获取样式

getComputedStyleelement.style 的相同点就是二者返回的都是 CSSStyleDeclaration 对象,取相应属性值得时候都是采用的 CSS 驼峰式写法

    <style>
      .box {
        width: 100px;
        height: 100px;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div id="box" class="box" style="border: 1px solid blue"></div>
    <script>
      /* 
            1.需要获取box的边框的样式  
            style属性只能获取行内样式
        */
      console.log(box.style.border);
      // 1px solid blue

      /* 
            2.获取盒子宽度  offset这些只能获取宽高之类
       */
      console.log(box.offsetWidth);
      // 102

      /* 
            3.获取背景颜色
            这个方法可以获取元素的所有样式,全局方法
      */

      const styleObj = getComputedStyle(box)
      console.log(styleObj)
    </script>

而不同点就是:

  • element.style 读取的只是元素的内联样式,即写在元素的 style 属性上的样式;而 getComputedStyle 读取的样式是最终样式,包括了内联样式嵌入样式外部样式
  • element.style 既支持读也支持写,我们通过 element.style 即可改写元素的样式。而 getComputedStyle 仅支持读并不支持写入。我们可以通过使用 getComputedStyle 读取样式,通过 element.style 修改样式

在这里插入图片描述

      /* 
            3.获取背景颜色
            这个方法可以获取元素的所有样式,全局方法
      */

      const styleObj = getComputedStyle(box)
      console.log(styleObj.border)
      console.log(styleObj.width)
      console.log(styleObj.backgroundColor)

在这里插入图片描述

​ 劣势:只有共处一个网段内(连接上你开启的wifi)的设备才能查看。

2.classList

.className
<div id="box" class="box" style="border: 1px solid blue"></div>
<script>
    // 这样写会覆盖之前的类名
    box.className = "one"
    // 要么就要加等于,但是还要注意空格
    box.className += " one"
.classListadd()
 // 添加类名
 box.classList.add("one")
 box.classList.add("two")

在这里插入图片描述

.classListremove()
  • 移除类名
<script>
    box.classList.add("one")
    box.classList.add("two")
    // 删除类名
    box.classList.remove("one")
</script>

在这里插入图片描述

.classList.toggle()
  • 切换类名
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值