移动WEB的硬知识

621 阅读13分钟

一、概述

APP开发目前有三种趋势,一种是原生APP,一种是网页APP,还有一种是H5APP。

移动web开发:

  • 跑在我们手机端的web页面,(H5页面)
  • 跨平台
  • 基于webview
  • 告别IE,拥抱webkit,兼容性问题不用担心
  • 更高的适配性,和性能要求
由于是基于webview,所以可以跨平台,不论是android还是ios,都能很好的呈现。webview可以理解为是我们的移动web页面和我们手机原生之间的一个中间桥梁。

二、像素

1、物理像素

也被称为设备像素 (Device independent pixels),即设备在出厂的时候就已经固定了像素。

我们来看一下 iPhone6 (左图) 与 iPhone6 plus (右图) 的官方显示屏的规格说明:

iPhone6 是 1334px x 750px 的像素分辨率,意思是当手机竖放的时候,横向有 750 个物理像素,纵向有 1334 个物理像素。

2、逻辑像素

在 CSS 中也被称为 CSS 像素 (CSS pixels),是为 Web 开发者创造的,在 CSS 和 JavaScript 中使用的一个抽象的层,每一个 CSS 声明和几乎所有的 Javascript 属性都使用 CSS 像素。

例如我们平时使用 Chrome 的设备调试工具的时候,iPhone6 是高 667px,宽是 375px,与苹果官方的 1334px x 750px,长宽分别少了 2 倍,那么面积就少了 4 倍。这就是经常说的 Retina 屏幕用四个(物理)像素表示一个(逻辑)像素。

3、像素密度PPI

我们看的屏幕都是由一个一个的像素点组成的,之前我们一直说的分辨率都是指实际像素尺寸,比如: 1920 * 1080就是 1920个像素点 * 1080个像素点。每个点发出不同的光就构成了我们所看的画面。

而到了我们的移动端就不一样了,因为我们的手机本身就那么宽,要放下一个较宽的页面,显然,这种上诉的像素排放就不合适了。

所以我们要引入一个概念了: 像素密度(PPI)

PPI: 准确的说是每英寸的长度上排列的像素点数量。1英寸是一个固定长度,等于2.54厘米.意思是一英寸中有多少个物理像素。

4、设备像素比(DPR)

我们再来看上面的图,同样的物理大小,PPI越高,所占据的像素点就越多,越高PPI的屏幕,显示一个像素点的面积就越小,一张由4x4个像素点组成的图显示在PPI为64的屏幕上,那么换到256PPI的屏幕上显示则会缩小为原来大小的一半。

反过来,如果要在PPI为256的屏幕上显示效果与PPI为64的屏幕一样,那么得要把图片放大2倍。

因此配有高清屏幕的手机,厂商为了其设备的可用性,即图标和文字可以被正确识别和准确点击,就必须保证各类素材在其设备上的显示与标清设备一样,而这个解决方法就是把所有尺寸都放大若干倍。这个放大比例就叫作设备像素比

因此高清设备上应该配有高清图片显示,不然图片在高清设备上放大后没有足够的像素显示其细节,那么这张图片就会变得看起来很模糊。

看上图,蓝色的为物理像素,绿色的为css像素。

在DPR为1(通常我们的PC),1px等于1个物理像素。DPR为2,1px等于2个物理像素。

总结:

1. 我们在开发中一般尽可能使用iphone 6s来当作测试页面
2. 设计师给我们的图纸一般都是使用该机型的像素尺寸来作图的
3. 我们的话一般还是使用物理尺寸(设备尺寸)来直观感受和设计页面
4. 由于iphone6s的缩放比例是2倍,因此我们使用的css像素就可以是设计图纸像素的一半。

三、viewport 视口

在移动端打开一个页面,如果浏览器先会以正常的比例来渲染页面,然后再自动地设置一个比例来缩放页面,目的是为了让内容更好地展示出来,即页面内容刚好铺满整个手机屏幕,当然如果页面没有禁止掉用户缩放的话,你也可以用两个手指把页面缩放回原始的比例。这整个过程就是通过视口(viewport)来实现的,原始页面渲染好后通过视口缩放使得与系统宽度一样,从而可以完整地展示页面。

移动端有三种视口:

布局视口: layout viewport
可视视口: visual viewport
理想视口: ideal viewport

因为移动端的屏幕尺寸都比较小,我们的页面根本放不下,这就有了布局视口的概念了,布局视口是虚拟的,一般都比较大,用户可以通过移动页面,缩放来通过可视视口查看页面的全部内容。

  • 存在的问题:

这时候虽然我们的页面被缩放,显示在了可视视口内了,但是我们的页面太小,容易影响用户的操作。

这时候就提出了理想视口,最初是由苹果公司提出的。理念:让我们的布局视窗,等于我们的理想视窗。然后把我们页面的元素正好放入到我们的页面中,元素的大小符合大众的阅读。

实现理想视窗,通常是:

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">

四、flex弹性布局

五、媒体查询

具体可看我的另一篇文章。 响应式设计之 —— 媒体查询

六、移动端适配

1、方案一:使用vm结合rem来实现适配

  • 前提: 我们的设计稿是750px的。

首先我们需要设置视窗

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

然后设置html根元素的字体大小。

html{font-size:13.33333333vw}

写的width:1rem,heigth:1remdiv就是50px* 50px (iphone6为2倍屏,即对应750px设计稿上的100px*100px) 怎么搞定的,1句代码就能实现,太神奇。

原理:

vw为屏幕宽度的1/100。我们的设计稿是750px的。那么100/750 = 0.1333333,那么1px就是0.1333333vw。100px就是13.333333vw了。这样我们就能愉快的写rem了。当我们html设置13.33333333vw的时候,我们的1rem 就等于设计稿的100px,就等于我们css像素的50px了。

  • 改进一下:

此方案只能兼容手机,甚至连ipad兼容都不好,当然,此处的兼容不是兼容问题,是效果问题,只要兼容vw的设备都能用这个方案,但是由于适配的根本是vw这个, 这个会随着设备的宽度越来越大,那么用rem做单位的元素也会越来越大,以至于如果这个在pc上,那么没法预览了,效果会很差,字太大了.这样我们可以设置一下当屏幕过大的时候的情况,我们可以加一句代码

@media (min-width: 560px) {
  html {
    font-size: 54px;
  }
}

2、方案二:Rem自适应js之精简版flexible.js

代码地址

/**
 * [ 移动端适配方案 ]
 * @params designWidth  设计稿的实际宽度值,需要根据实际设置
 * @params maxWidth     制作稿的最大宽度值,需要根据实际设置
 * 例如设计稿为750,最大宽度为750,则为(750,750)
 * 拿iphone6作为实验机: 像素宽度为 750像素  css宽度为375px  最终设置html:font-size:50px;  1rem = 50px;  375px  = 7.5rem
 * 如果设计稿是750像素宽的,由于dpr = 2,所以逻辑像素宽度只有 375 ,那么设计图上的 100px 代表 1rem;
 */
;
(function(designWidth, maxWidth) {
  var doc = document,
    win = window,
    docEl = doc.documentElement, // html节点
    remStyle = document.createElement("style"),
    tid;

  // 计算 rem 比例
  function refreshRem() {
    // getBoundingClientRect 获取某个元素相对于视窗的位置集合
    var width = docEl.getBoundingClientRect().width; // 获取整个文档元素的宽度,iphone6 是 375
    maxWidth = maxWidth || 540;
    width > maxWidth && (width = maxWidth); // 如果整个视窗的宽度大于 设计稿的最大宽度,那么就设置宽度为设计稿的最大宽度,
    var rem = ( width / designWidth ) * 100; // 不同的手机,width不一样,计算 width / designWidth 为不同的比例,比如 iphone6 下 rem = 50px iphone5 下 rem = 42.66666666666667px;
    // 利用该效果来实现屏幕小就缩小,屏幕变大就适当的放大
    remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';
  }

  // 将 style 标签引入
  if (docEl.firstElementChild) {
    docEl.firstElementChild.appendChild(remStyle); // 头部head中插入
  } else {
    var wrap = doc.createElement("div");
    wrap.appendChild(remStyle);
    doc.write(wrap.innerHTML);
    wrap = null;
  }
  // 要等 wiewport 设置好后才能执行 refreshRem,不然 refreshRem 会执行2次;
  refreshRem();

  // 手机屏幕尺寸变化
  win.addEventListener("resize", function() {
    clearTimeout(tid); //防止执行两次
    tid = setTimeout(refreshRem, 300);
  }, false);

  // 页面返回自动刷新
  win.addEventListener("pageshow", function(e) {
    if (e.persisted) { // 浏览器后退的时候重新计算
      clearTimeout(tid);
      tid = setTimeout(refreshRem, 300);
    }
  }, false);

  // 文档全部资源加载完成
  // 为body设置默认字体大小为16ox
  if (doc.readyState === "complete") {
    doc.body.style.fontSize = "16px";
  } else {
    doc.addEventListener("DOMContentLoaded", function(e) {
      doc.body.style.fontSize = "16px";
    }, false);
  }
})(750, 750);

使用方法:

  • 1.复制上面这段代码到你的页面的头部的script标签的最前面。

  • 2.根据设计稿大小,调整里面的最后两个参数值。

  • 3.(这里假设你的设计稿是750像素)使用1rem=100px转换你的设计稿的像素,例如设计稿上某个块是100px*300px,换算成rem则为1rem*3rem

demo:

// demo.html

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <!--必须要设置该项-->
  <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
  <title></title>
  <link rel="stylesheet" href="./index.css">
</head>

<body>
  <div class="box">
    我是盒子
  </div>
  <div class="test">
  我是盒子
  </div>
  <script src="./flexible.me.js"></script>
</body>

</html>

3、方案三:媒体查询修改html字体

代码地址

既然rem是根据html字体大小来定,我们我们完全也可以通过css3媒体查询来完成这部分工作。

@media screen and (min-width:320px) {
  html {
    font-size: 21.33px
  }

  body {
    font-size: 12px
  }
}

@media screen and (min-width:360px) {
  html {
    font-size: 24px
  }

  body {
    font-size: 12px
  }
}

@media screen and (min-width:375px) {
  html {
    font-size: 25px
  }

  body {
    font-size: 12px
  }
}

@media screen and (min-width:384px) {
  html {
    font-size: 25.6px
  }

  body {
    font-size: 14px
  }
}

@media screen and (min-width:400px) {
  html {
    font-size: 26.67px
  }

  body {
    font-size: 14px
  }
}

@media screen and (min-width:414px) {
  html {
    font-size: 27.6px
  }

  body {
    font-size: 14px
  }
}

@media screen and (min-width:424px) {
  html {
    font-size: 28.27px
  }

  body {
    font-size: 14px
  }
}

@media screen and (min-width:480px) {
  html {
    font-size: 32px
  }

  body {
    font-size: 15.36px
  }
}

@media screen and (min-width:540px) {
  html {
    font-size: 36px
  }

  body {
    font-size: 17.28px
  }
}

@media screen and (min-width:720px) {
  html {
    font-size: 48px
  }

  body {
    font-size: 23.04px
  }
}

@media screen and (min-width:750px) {
  html {
    font-size: 50px
  }

  body {
    font-size: 24px
  }
}

像苏宁易购等网站就是这么做的:

4、方案四:JS动态计算html字体大小

代码地址

通过JavaScript读取屏幕宽度,然后根据宽度计算出对应的尺寸并设置根元素的font-size

我们以320px的屏幕基准像素为12px为准则。

let htmlwidth = document.documentElement.clientWidth || document.body.clientWidth ; // 获取文档宽度
let htmlDom = document.getElementsByTagName('html')[0]; // 获取htmldom
// 如果页面宽度太大了的话,保持750
if(htmlwidth>750){ htmlwidth = 750}
// 320px的屏幕基准像素为12px
htmlDom.style.fontSize= 12 * (htmlwidth / 320) + "px";

使用JS来获取屏幕宽度的好处在于可以100%适配所有的机型宽度,因为其元素的基准尺寸是直接算出来的。既然是JS代码,为了避免造成因为动态设置元素的font-size而造成页面抖动,一般这部分代码我们放在header底部去加载,并内联到html文档里面。

七、touch事件

1、事件类型

touchstart  手指接触屏幕时触发
touchend    手指离开屏幕时触发
touchmove   已经接触屏幕的手指开始移动的时候触发
touchcancel 某种touch事件非正常结束时触发

2、事件对象

有三个TouchList,一个TouchList 代表一个触摸平面上所有触点的列表。

touches
targetTouches
changedTouches

touches

列出所有当前在与触摸表面接触的 Touch 对象

targetTouches

包含所有仍与触摸平面接触并且touchstart事件与当前事件在同一个目标元素上发生的Touch对象。

changedTouches

用于记录想要改变最开始接触摸平面的触摸对象的touch对象。

通过上图我们分析以下:

当只有一个元素的时候,三个touchList都是相同的。

当我们的目标是div2,一次上来三次触摸分别为1,2,3。这时候touches记录全部的触摸点,targetTouches,记录所在目标上的所有触摸点,changedTouches记录第一个触摸点之后的触摸点。

使用touch事件可以实现多种效果,比如拖拽,多点触摸,旋转手势等。

touch手势实例

3、touch对象

clientX:65 // 触摸点在浏览器窗口中的横坐标
clientY:18 // 触摸点在浏览器窗口中的纵坐标

force:1 // 触摸点压力大小
identifier:0 // 触摸点唯一标识(ID)

pageX:65 // 触摸点在页面中的横坐标
pageY:18 // 触摸点在页面中的纵坐标

radiusX:11.5 // 触摸点椭圆的水平半径
radiusY:11.5 // 触摸点椭圆的垂直半径

rotationAngle:0 // 旋转角度

screenX:560 // 触摸点在屏幕中的横坐标
screenY:175 // 触摸点在屏幕中的纵坐标

4、点击300ms延迟

最初的苹果公司为了让用户可以在手机上可以看我们的页面,允许用户双击去放大效果,当用户触摸第一次之后,会在300ms之内去监听是否有下一次的触摸,用来判断是双击操作还是单击操作,这就是300ms延迟的由来。

解决办法有:vuefastclickreactreact-tap这两种。

5、点击穿透

点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件蒙层关闭按钮绑定的是touch事件,而按钮下边元素绑定的是click事件,touch事件触发之后,蒙层消失了,300ms后这个点的click事件fire,event的target自然就是按钮下面的元素,因为按钮跟蒙层一起消息了。

解决办法就是touch和click事件最好不要混用。