基础概念

屏幕尺寸



  • 指屏幕的对角线的长度,单位是英寸,1英寸 = 2.54厘米
  • 常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等




移动端app部署架构_移动端

 

屏幕分辨率

  • 在横纵向上的像素点的个数,单位是px,1px = 1个像素点。
  • 一般以 纵向像素 * 横向像素 来表示一个手机的分辨率,比如 1960*1080(这里的1像素指的是物理设备的1个像素点)

高清屏

  • 高清屏(Retina:又称视网膜屏)与普通屏相比,相同区域的物理像素点数是普通屏的4倍(DPR=2)
  • 具备足够高的物理像素密度而使人肉眼无法分辨其中单独像素点的液晶屏
  • 是一种具备超高像素密度的液晶屏
  • 同样大小屏幕上显示的像素点由一个变为多个

物理像素

  • 又称为设备像素,它是屏幕能显示的最小粒度
  • 买手机的时候会有一个 n x m 的分辨率, 那是屏幕的 n x m 个呈像的点,一个点(小方格)为一个物理像素
  • 设备像素也被称为物理像素,它是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和高度
  • 任何设备的物理像素都是固定的,任何一款设备上1物理像素的大小是不会变得
  • 不同设备上1物理像素的大小可能是不一样的

CSS像素

  • CSS像素是一个抽象的单位,主要使用在浏览器上,用来精确的度量Web页面上的内容
  • 它是为Web开发者创造的,在css或者javascript中使用的一个抽象的层
  • 一般情况下,css像素被称为与设备无关的像素(device-independent像素),简称"DIPs"
  • 在一个标准的显示密度下,一个css像素对应着一个设备像素

像素中的最小单位

  • 物理像素是设备呈像的最小单位
  • css像素是浏览器中的最小单位(即web开发中的最小单位)
  • 位图像素是图像的最小单位

物理像素与CSS像素的关系

  • 一个width为200px的元素,它占据了200个css像素,但200个css像素占据多少个物理像素取决于屏幕的特性(是否是高密度,即像素比)和用户的缩放行为
  • 在苹果的视网膜屏幕上,视网膜的像素密度是普通屏幕的两倍,这个元素就跨越了400个设备像素
  • 如果用户进行放大操作,视觉视口尺寸变小(尺寸:放的css像素个数),它将跨越更多的设备像素
  • 需要注意的是,css像素与物理像素的关系是靠浏览器厂商在维护,并不是设备厂商
  • css像素是浏览器中特有的概念

屏幕像素密度

  • 又称像素密度或屏幕密度
  • 屏幕上每英寸可以显式的像素点的数量,单位是ppi(pixels per inch)
  • 屏幕像素密度与屏幕尺寸和屏幕分辨率有关

设备独立像素

  • 设备独立像素,又称密度物管像素,可以理解为是计算机坐标系统中的一个点
  • 这个点代表一个可以由程序使用的虚拟像素(比如:css像素),然后由相关系统转换为物理像素
  • 它是css像素转换物理像素过程中很重要的一个环节
  • 设备独立像素可以看成是设备提供的接口,用于对接应用的一些概念(如浏览器中的css像素)
  • 只有当浏览器厂商对接上设备的独立像素时,浏览器厂商设计的移动端规则才能起作用,否则将采取默认的规则

位图像素

  • 1个位图像素是栅格图像(如:png,jpg,gif等)最小的数据单元
  • 只有当1个位图像素对应1个物理像素时,图片才能得到完美清晰的展示

像素比(DPR)

  • 像素比指物理像素和设备独立像素的比例(即:devicePixelRatio = 物理像素 / 设备独立像素)
  • 像素比 = 一个方向上占满一块屏幕需要的物理像素的个数/一个方向上占满一块屏幕需要的设备独立像素的个数
  • 注意,像素比是比较个数而不是比较面积
  • 添加 <meta> 标签 width=device-width
  • 像素比可以通过  window.devicePixelRatio 获取

移动端视口

移动端浏览器通常宽度是 240px~640px,而大多数为 PC 端设计的网站宽度至少为 800px,如果仍以浏览器窗口作为视口的话,网站内容在手机上看起来会非常窄。因此,引入了布局视口、视觉视口和理想视口三个概念,使得移动端中的视口与浏览器宽度不再相关联。

布局视口(layout viewport)

  • 布局视口的宽度/高度可以通过 document.documentElement.clientWidth / Height
  • 布局视口使视口与移动端浏览器屏幕宽度完全独立。CSS 布局将会根据它来进行计算,并被它约束
  • 布局视口用于解决早期的pc端页面在手机上显示的问题。
  • iOS, Android 基本都将这个视口分辨率设置为 980px,所以 PC 上的网页基本能在手机上呈现,只不过元素看上去很小

移动端app部署架构_移动端_02

视觉视口(visual viewport)

  • 视觉视口是用户当前看到的区域,用户可以通过缩放操作视觉视口,同时不会影响布局视口。
  • 当前缩放值 = 理想视口宽度 / 视觉视口宽度
  • 视觉视口决定了物理像素和css像素之间的比例

移动端app部署架构_css_03

理想视口

  • 只有当我们在页面中加入viewport的meta标签时,理想视口才会生效 <meta name="viewport" content="width=device-width">
  • 理想视口包含的css像素的个数等于独立设备像素的值
  • 理想视口由浏览器厂商定义,因此同一设备上的不同浏览器拥有不同的理想视口
  • 浏览器理想视口的大小也取决于设备,同一款浏览器在不同设备上拥有不同的理想视口

完美视口

  • 当我们在页面中加入viewport的 <meta name="viewport" content="width=device-width;initial-scale=1.0">
  • width=device-width 和 initial-scale=1.0
  • 完美视口不仅仅只能解决旋转时的问题,当页面中存在太大的元素时,假如使用理想视口,某些浏览器会扩展布局是口的宽度来容纳这个元素。此时,使用完美视口可以规避这个问题
  • 推荐写法:
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>



布局视口与理想视口的关系

  • 布局视口只规定出现在视口元素中的排列规则,它仅仅是移动端浏览器的一个属性。在物理像素和css像素1:1的情况下,可以认为布局视口比设备宽度(分辨率)要大很多
  • 视觉视口决定了用户能看到什么
  • 从css像素角度出发,由于视觉视口所包含的css像素的个数跟用户的缩放行为有关,我们无法准确的判断视觉视口的尺寸。
  • 默认情况下(物理像素:css像素 = 1:1),一个视觉视口包含该设备分辨率的值所代表的css像素个数
  • 视觉视口包含了整个布局视口
  • 移动端在初始化时,视觉视口会将整个布局视口完整显示出来,所以移动端浏览器在初始化的时候,物理像素与css像素之间的比例不可能是一比一,这个比例与设备分辨率和布局是口的大小有关
  • 这个比例的本质上和视口有关,因为移动端浏览器初始化时视觉视口包含的css像素个数等于布局视口包含的css像素个数
  • 视觉视口包含的物理像素个数等于设备的分辨率

获取三个视口的宽度(移动端)

  • 布局视口
  • var 
  • 无兼容性问题
  • 视觉视口
  • var 
  • 接近全部支持
  • 理想视口
  • var 
  • 一些设备代表理想视口宽度,一些设备代表设备的分辨率,有极大的兼容性问题

缩放

用户缩放

  • 只影响布局视口
  • 放大:一个css像素的面积变大,区域内css像素的个数变少,视觉视口的尺寸变小
  • 缩小:一个css像素的面积变小,区域内css像素的个数变多,视觉视口的尺寸变大
  • 在pc端用户缩放影响视口的尺寸
  • 在移动端用户缩放影响视觉视口的尺寸

系统缩放

  • 可以通过<meta>设置系统缩放
  • 影响布局视口和视觉视口

meta&viewport

  • 此标签桌面浏览器不支持
  • 推荐写法: <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
  • width [pixel_value|device-width]
  • 用于控制布局视口的大小, width=device-width
  • 浏览器支持width的最大值为10000个像素,最小值为理想视口的20%
  • 设置具体数值大兼容性不佳,大部分的安卓手机不支持,仅有ios支持
  • 安卓的webkit不允许小于布局视口的宽度,如果指定了一个小于布局视口的值,它会默认转为布局视口的大小(通常为980)
  • ie10不允许超出480px的值,超出自动设置为布局视口的默认宽度1020px
  • 95%的浏览器都支持 width=device-width
  • initial-scale
  • 初始缩放比例
  • 当用户旋转设备时,布局视口的宽度通常会发生改变,因为横竖屏时的理想视口尺寸不一样,但ios的Safari的视口不会做出改变(基于性能的考虑)
  • ios的Safari在 inital-scale 设置的情况下,用户旋转设备,其布局视口会发生改变。但只设置 inital-scale
  • user-scalable [no|yes]
  • minimum-scale:允许缩放的最小比例
  • maximum-scale:允许缩放的最大比例
  • 没有通过meta标签设置 minimum-scale 和 maximum-scale
  • 有通过meta标签设置 minimum-scale 和 maximum-scale
  • 安卓webkit不支持 minimum-scale
  • ie minimum-scale 和 maximum-scale
  • target-densitydpi
  • dpi_value 70-400:每英寸像素点的个数
  • device-dpi:设置默认像素密度
  • high-dpi:高像素密度
  • medium-dpi:中等像素密度
  • low-dpi:低像素密度

 移动端适配方案

  • rem适配原理:改变了一个元素在不同设备上占据的css像素的个数
  • rem单位:根标签的font-size所代表的值,即根标签的fontsize = 1rem
  • rem适配优缺点
  • 优点:没有破坏完美视口
  • 缺点:px值到rem的转换太复杂
  • rem与em的区别
  • em
  • 子元素字体大小的em是相对于父元素字体大小
  • 元素的width/height/padding/margin用em的话是相对于该元素的font-size
  • rem
  • rem是全部的长度都相对于根元素,即<html>元素
  • 示例
(function () {
      var layout = document.documentElement.clientWidth / 16;
      var styleNode = document.createElement('style'); // 创建style标签
      styleNode.innerHTML = "html{font-size : " + layout + "px!important"; // 将根标签的font-size置为布局视口的宽/16
      document.head.appendChild(styleNode); //将style标签添加到head中
})()

 viewport适配

  • viewport适配原理
  • 将所有设备布局视口的宽度调整为设计图的宽度
  • 修改css像素与布局像素的比例,每一个元素在不同设备上占据的css像素的个数是一样的。但是css像素和物理像素的比例不相同
  • 适配比例 = 理想视口宽度 / 设计图尺寸
  • viewport适配方案的优缺点
  • 优点:所量即所得
  • 缺点:不是完美视口
  • 获取理想视口的宽度
  • 方法一: screen.width ---- 兼容性差
  • 方法二:一开始给HTML标签添加上<meta name="viewport" content="width=device-width"> 。此时,用document.documentElement.clientWidth获取理想视口的宽度
  • 示例
(function () {
      var targetW = 640 // 设计图宽度
      var scale = document.documentElement.clientWidth / targetW // 理想视口的宽度 / 设计图宽
      var meta = document.querySelector("meta[name='viewport']")
      meta.content = 'initial-scale=' + scale + ',minimum-scale=' + scale + ',maximum-scale='+scale+',user-scalable=no'
})()

移动端一物理像素实现

rem+系统缩放

  • 主体适配采用rem适配 并放大rem的基值(dpr倍)
  • 再通过系统缩放 缩回dpr倍,initial-scale=1/dpr
  • 示例
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0,user-scalable=no,maximum-scale=1.0,minmul-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>1物理像素</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #test {
      width: 16rem;
      height: 1px;
      background-color: black;
      margin-top: 30px;
    }
  </style>
</head>

<body>
  <div id="test"></div>

  <script>
    // rem 适配
    // 淘宝的做法,通过适配角度解决1物理像素的问题
    window.onload = function () {
      (function () {
        var dpr = window.devicePixelRatio || 1 // 像素比
        var layout = document.documentElement.clientWidth // 布局视口宽度

        var styleNode = document.createElement('style')
        styleNode.innerHTML = "html { font-size:" + layout * dpr / 16 + "px!important}"
        document.head.appendChild(styleNode)

        var scale = 1 / dpr
        var meta = document.querySelector('meta[name="viewport"]')
        meta.content = "width=device-width, initial-scale=" + scale + ",user-scalable=no,maximum-scale=" + scale + ",minimum-scale=" + scale
      })()
    }
  </script>
</body>
</html>

响应式+变换缩放

  • 通过媒体查询进行单个元素的缩放
  • 示例
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0,user-scalable=no,maximum-scale=1.0,minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>1物理像素</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #test {
      width: 100%;
      height: 1px;
      background-color: black;
      margin-top: 5rem;
    }

    @media only screen and (-webkit-device-pixel-ratio:2) {
      #test {
        transform: scaleY(0.5);
      }
    }

    @media only screen and (-webkit-device-pixel-ratio:3) {
      #test {
        transform: scaleY(0.33333);
      }
    }
  </style>
</head>

<body>
  <div id="test"></div>

  <script>
    (function () {
      var layout = document.documentElement.clientWidth / 16;
      var styleNode = document.createElement('style');
      styleNode.innerHTML = "html{font-size :" + layout + "px!important;}"
      document.head.appendChild(styleNode)
    })()
  </script>
</body>

</html>

移动端事件基础

触摸事件

  • 移动端有两类事件: 触摸事件、 指针事件[←点击可查看MDN文档]
  • 本篇暂时只提到触摸事件中的touchstart、touchmove以及touchend
  • touchmove与mousemove的区别
  • touchmove:不可能单独触发
  • mousemove:可以单独触发
  • 需要注意的是:touchstart、touchmove以及touchend最好不要用DOM1的事件绑定 item.ontouchstart = function(){}
  • addEventListener()

 禁止移动端事件默认行为



  • 需要全面禁止的系统默认行为有:
  • 下拉橡皮泥(阻尼)效果
  • 长按选中文字效果
  • 示例
// 下面这行代码可以禁止手机浏览器所有的默认事件 
      document.addEventListener("touchstart", function (ev){
        ev = ev || window.event
        // ev.cancelable 可用于查看是否能取消该事件的浏览器默认行为
        item.innerHTML = ev.cancelable;
        ev.preventDefault()
})



 移动端初始模板

  • 移动端必备条件
  • meta标签
  • 全面阻止事件的默认行为
  • 全面阻止事件的默认行为,有一些设备会有页面上所有的滚动条失效的隐患(可以使用自定义滚动条)
  • 适配方案
  • 示例
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width,initial-scale=1.0,user-scalable=no,minimum-scale=1.0,maximum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <script>
    window.onload = function () {

      // 阻止默认行为
      document.addEventListener('touchstart', function (ev) {
        ev = ev || window.event
        ev.preventDefault()
      })

      // rem适配方案
      !(function (flag) {
        var w = document.documentElement.clientWidth
        var styleNode = document.createElement('style')
        styleNode.innerHTML = "html{font-size: " + w / flag + "px!important}"
        document.head.appendChild(styleNode)
      })(16)
    }  
  </script>
</body>

</html>

事件穿透

  • 事件穿透产生的原因
  • pc端的事件可以在移动端触发
  • pc端事件有300毫秒延迟
  • 移动端事件不会有延迟
  • 示例
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width,initial-scale=1.0,user-scalable=no,minimum-scale=1.0,maximum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body,
    html {
      line-height: 0;
    }

    .wrapper {
      width: 200px;
      height: 200px;
      background-color: skyblue;
      opacity: 0.5;
      position: absolute;
      top: 0;
      left: 0;
    }

    a {
      font-size: 12px;
      line-height: 18px;
    }
  </style>
</head>

<body>
  <div class="wrapper"></div>
  <a href="http://www.baidu.com">百度一下</a>

  <script>
    window.onload = function () {
      // 阻止默认行为
      document.addEventListener('touchstart', function (ev) {
        ev = ev || window.event
        ev.preventDefault()
      })

      // rem适配方案
      !(function (flag) {
        var w = document.documentElement.clientWidth
        var styleNode = document.createElement('style')
        styleNode.innerHTML = "html{font-size: " + w / flag + "px!important}"
        document.head.appendChild(styleNode)
      })(16)

      var wrapper = document.querySelector('.wrapper')
     
      wrapper.addEventListener('click', function () {
        wrapper.style.display = "none"
      })

      // 全面禁止事件默认行为会导致手机端a标签无法跳转
      //  移动端a标签跳转方案,解决误触
      var aNodes = document.querySelectorAll("a")
      for (var i = 0; i < aNodes.length; i++) {
        // 该写法使滑动的时候页面不发生跳转
        aNodes[i].addEventListener('touchstart', function () {
          this.isMove = false;
        })

        aNodes[i].addEventListener('touchmove', function () {
          this.isMove = true;
        })

        aNodes[i].addEventListener('touchend', function () {
          if (!this.isMove) {
            window.location = this.href
          }
        })
      }
    }  
  </script>
</body>

</html>

TouchEvent

touchEvent.changedTouches
  • 触发事件时改变的触摸点的集合
  • 可以通过clientX和clientY属性来获取当前集合中手指在视口中的坐标的坐标
  • touchEvent.changedTouches[0].clientX; // 获取当前集合中第一根手指的横坐标
touchEvent.targetTouches
  • 绑定事件的那个结点上的触摸点的集合列表
touchEvent.touches
  • 触发事件时改变的触摸点的集合

移动端常见问题

禁止电话与邮箱

  • iPhone上的Safari和某些webkit android手机浏览器,会自动对看起来像是电话号码的数字串(包括已经加入连字符或括号格式化过的)和邮箱添加链接,导致用户点击该数字后跳转到拨打电话的界面
  • 可以通过使用meta标签来 禁止掉电话及 邮箱的跳转 <meta name="format-detection" content="telephone=no,email=no">
  • 当禁止电话及邮箱跳转后如果想让用户点击电话或邮箱跳转可以将其写成如下格式
<a href="tel:110">13579246801</a>
<a href="mailto:123@qq.com">123@qq.com</a>

链接高亮



a {
    text-decoration: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0); /* 解决链接高亮问题 */
}



圆角过圆



input {
    width: 50px;
    height: 50px;
    border-radius: 5px;
    -webkit-appearance: none; /* iphone 只要有 border-radius 属性就会变成一个圆  */
}



Font Boosting

  • Font Boosting被称为字体提升,也被称为Text Autosizer,Font Inflation.是 Webkit 给移动端浏览器提供的一个特性:当我们在手机上浏览网页时,很可能因为原始页面宽度较大,在手机屏幕上缩小后就看不清其中的文字了。而 Font Boosting 特性在这时会自动将其中的文字字体变大,保证在即不需要左右滑动屏幕,也不需要双击放大屏幕内容的前提下,也可以让人们方便的阅读页面中的文本。
  • 解决方法一:给元素指定宽高
  • 解决方法二:给元素指定max-height p { max-height: 9999px; }

补充

Web App、Native App、hybrid app

  • web App:即web前端页面,多采用h5开发
  • 不需要安装包,节约手机空间
  • 量级轻,开发成本低
  • 版本迭代快
  • 基于浏览器,可以跨平台使用
  • 安全性相对较低,页面跳转费力,不稳定感更强。
  • native app:又称原生app,基于智能手机本地操作系统android(安卓基于Java) 、ios(基于OC --- Object-C)编写运行的第三方应用程序。
  • 性能更好
  • 版本迭代需要用户重新打包补丁
  • 开发成本高。
  • 发布新版本慢,应用商店发布审核周期长,下载由用户控制
  • hybrid app:即混合app, native app 与 web app的混合。

 移动价值链



  • 网络运营商
  • 中国移动
  • 中国联通
  • 中国电信
  • 设备供应商
  • 华为
  • 小米
  • 魅族
  • oppo
  • vivo
  • 苹果
  • ...
  • 软件制造商(操作系统)
  • 谷歌
  • 苹果
  • ...
  • 服务提供商
  • 谷歌
  • 华为
  • 小米
  • ...

移动端浏览器分类

  • 移动端大概有30多种浏览器,其中20多种处于边缘化状态
内置浏览器
  • 每部手机都有内置浏览器,这个浏览器属于设备的固件,通常由操作系统厂商开发,而且大多数内置浏览器都被紧密的集成到了底层的操作系统中去了,因此我们没有办法单独升级内置浏览器,只能借助于更新操作系统。总的来说,它的特点如下:
  • 更新慢
  • 移植在操作系统中
  • 安卓
  • 三星 --- 三星webkit
  • 中兴 --- 中兴webkit
  • 华为 --- 华为webkit 
  • 小米 --- 小米webkit
  • 索尼 --- 索尼webkit
  • ios --- safari
  • 黑莓 --- 黑莓webkit
  • window phone --- IE
可下载浏览器
  • 在实践中,只有安卓才有可下载浏器(uc、猎豹、qq浏览器等)
  • 可下载浏览器更新快
  • 可下载浏览器独立于操作系统
webview
  • webview是独立程序,是留给原生应用的一个操作系统浏览接口,它用了内置浏览器很多底层的组件(比如渲染引擎)
  • ios的操作系统默认不允许在它身上有多余的渲染引擎,因此其他浏览器想在ios上运行就必须使用ios的webview
代理浏览器(较少)
  • 代理浏览器的渲染引擎存在于服务端,因此其js性能及其低下
  • 代理浏览器的渲染引擎能够解析和执行 HTML、CSS、Javascript,但并不是运行在设备上,而是在远程服务器上,与代理浏览器相对应的叫完备浏览器
  • 完备浏览器
  • 它与我们预期的浏览器的运行方式一样,当用户请求一个页面时,浏览器就会通过http请求去抓取HTML CSS Javascript 和其他资源,一旦一切就绪,就会去渲染和显示页面,所有的步骤都是在客户端进行的,会占用内存,处理器的事件,电池寿命
  • 混合浏览器
  • 可以在代理浏览器和完备浏览器间切换的浏览器称为混合浏览器(opera-mini、uc-mini)
  • 代理浏览器工作流程
  • 用户请求一个页面,他不会发送一个普通的http请求,而是发送一个特殊的加密链接到一个特殊的代理服务器
  • 代理服务器会发送正常的http请求给用户希望访问的web服务器
  • 代理服务器包含一个渲染引擎,能够正常渲染页面
  • 代理服务器压缩页面,压缩页面后的文件类似于PDF,它有链接热点
  • 代理服务器同样通过加密链接把这个文件发到客户端
  • 客户端展示