# 移动端事件

在1996年,Netscape(网景)引入了鼠标事件和著名的鼠标悬停事件,使得Web开发者能够在PC端开发出可交互的网站。随后引入了键盘事件,能够让我们在网页中监控到用户的输入动作。这两种事件在PC端统治了长达15年,一直到iOS设备的出现,它既没有鼠标也没有键盘,所以在为移动Safari开发交互网页时,常规的鼠标和键盘事件根本不够用,于是又了第三种时间-触摸事件。随着Android中的WebKit的加入,很多这样的专有事件变成了事实标准。

# 移动端常用事件

事件 描述
click 当点击时触发(单击)
load 页面结束加载之后触发
scroll 当元素滚动条被滚动时运行的脚本
blur 元素失去焦点时运行的脚本
focus 当元素获得焦点时运行的脚本
change 在元素值被改变时运行的脚本
input 代替keyup、keydown
touch 处理单手指操作
<!-- gesture

# touch事件模型(手势事件)

事件 描述
touchstart 当手指触摸屏幕时候触发,即使已经有一个手指放在屏幕上也会触发。
touchmove 当手指在屏幕上滑动的时候连续地触发。在这个事件发生期间,调用preventDefault()事件可以阻止滚动
touchend 当手指从屏幕上离开的时候触发
touchcancle 特殊情况下关闭/退出时触发

触摸事件的事件对象

我们可以同时又很多手指触摸屏幕,所以在事件对象里面给我们提供了相应的数组来存储每个手指的信息。

  • touches:表示当前跟踪的触摸操作的touch对象的数组。
  • targetTouches:特定于事件目标的Touch对象的数组。
  • changeTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组。

事件对象属性

属性 描述
clientX 触摸目标在视口中的x坐标
clientY 触摸目标在视口中的y坐标
identifier 标识触摸的唯一ID
pageX 触摸目标在页面中的x坐标
pageY 触摸目标在页面中的y坐标
screenX 触摸目标在屏幕中的x坐标
screenY 触摸目标在屏幕中的y坐标
timeStamp 从页面打开到当前时间触发所经过的毫秒数
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 // 触摸点在屏幕中的纵坐标
Copied!

# DeviceMotionEvent事件

DeviceMotionEvent为web开发者提供了关于设备的位置和方向改变的速度的信息。
目前,Firefox 和 Chrome 处理坐标的方式不同。 在使用它们的时候要多加注意。

# 原理

开发者从各个内置传感器那里获取未经修改的传感数据,并观测或响应设备各种运动和角度变化。这些传感器包括陀螺仪、加速器和磁力仪(罗盘)。

加速器和陀螺仪的数据都是描述沿着iOS设备三个方向轴上的位置,对于一个竖屏摆放的iPhone来说,X方向从设备的左边(负)到右边(正),Y方向则是由设备的底部(-)到顶部(+),而Z方向为垂直于屏幕由设备的背面(-)到正面(+)。

DeviceMotionEvent 会在设备发生有意义的摆动(或运动)时产生.事件对象封装有产生的间距值,旋转率,和设备加速度.

加速度的计算方式是重力和用户产生的两个加速度矢量之和.设备是通过 陀螺仪和加速计来区别这两者的.

通过DeviceMotion对设备运动状态的判断,则可以帮助我们在网页上就实现“摇一摇”的交互效果。

# 属性(只读)

属性 描述
DeviceMotionEvent.acceleration 提供了设备在X,Y,Z轴方向上加速度的对象。加速度的单位为 m/s2。
DeviceMotionEvent.accelerationIncludingGravity 提供了设备在X,Y,Z轴方向上带重力的加速度的对象。加速度的单位为 m/s2
DeviceMotionEvent.rotationRate 提供了设备在 alpha,beta, gamma轴方向上旋转的速率的对象。旋转速率的单位为 ?°/s 。
DeviceMotionEvent.interval 表示从设备获取数据的频率,单位是毫秒。
window.addEventListener('devicemotion', function(event) {
  console.log(event.acceleration.x + ' m/s2');
});
Copied!

# 手势库

# HammerJS

# 简介

用于检测触摸手势的 JavaScript 库
添加对触摸手势的支持并移除了点击的 300ms
支持最常见的单点和多点触摸手势,并且可以完全扩展以添加自定义手势

安卓触屏上,tap 和 click 可以同时触发,但是 click 会有 300ms 左右的延时

# 兼容性

查看兼容:http://hammerjs.github.io/browser-support/

# 下载安装

点击下载:http://hammerjs.github.io/dist/hammer.min.js

# 基本使用

<div id="test" class="test"></div>
<script type="text/javascript">
       //创建一个新的hammer对象并且在初始化时指定要处理的dom元素
       var hammertime = new Hammer(document.getElementById("test"));
       //为该dom元素指定触屏移动事件
       hammertime.on("pan", function (ev) {
           //控制台输出
           console.log(ev);
       });
</script>
Copied!

hammer.js 提供了 tap, doubletap, press, horizontal pan, swipe 和多点触控的 pinch, rotate,默认情况下 pinch 与 rotate 是禁用的,因为它们会阻塞元素,可以通过以下命令来启用。

hammertime.get('pinch').set({ enable: true });
hammertime.get('rotate').set({ enable: true });
Copied!

默认的 pan 仅支持水平方向,可以选择启用全部方向,swipe 也可以启用垂直方向

hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
hammertime.get('swipe').set({ direction: Hammer.DIRECTION_VERTICAL });
Copied!

建议使用 viewport meta 禁用双击/缩放功能,更多的控制网页,支持触摸操作的浏览器不需要这样做

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

# 手势总结

HammerJS

  1. tap
    在指定的 DOM 区域内,一个手指轻拍或点击时触发该事件(类似 PC 端的 click),该事件最大点击时间为 250ms,如果超过 250ms 则按 press 事件处理
  2. press 在指定的 DOM 区域内,这个事件相当于 PC 端的 Click 事件,不能包含任何的移动,最小按压时间为 500ms,常用于我们在手机上用的复制粘贴等功能
    该事件事分别对以下事件进行监听并处理
    • pressup: 点击事件离开时触发
  3. pan 在指定的 DOM 区域内,一个手指放下并移动事件,即触屏中的拖动事件
    该事件事分别对以下事件进行监听并处理
    • panstart: 拖动开始
    • panmove: 拖动过程
    • panend: 拖动结束
    • pancancel: 拖动取消
    • panleft: 向左拖动
    • panup: 向上拖动
    • pandown: 向下拖动
  4. swipe 在指定的 DOM 区域内,一个手指快速的在触屏上滑动,即平时用到最多的滑动事件
    该事件事分别对以下事件进行监听并处理
    • swipeleft: 向左滑动
    • swiperight: 向右滑动
    • swipeup: 向上滑动
    • swipedown: 向下滑动
  5. pinch 在指定的 DOM 区域内,两个手指(默认为两个手指,多指触控需要单独设置)或多个手指相对(越来越近)移动或相向(越来越远)移动时事件
    该事件事分别对以下事件进行监听并处理
    • pinchstart: 多点触控开始
    • pinchmove: 多点触控过程
    • pinchend: 多点触控结束
    • pinchcancel: 多点触控取消
    • pinchin: 多点触控时两手指越来越近
    • pinchout: 多点触控时两手指越来越远
  6. rotate 在指定的 DOM 区域内,当两个手指或更多手指呈圆型旋转时触发
    该事件事分别对以下事件进行监听并处理
    • rotatestart: 旋转开始
    • rotatemove: 旋转过程
    • rotateend: 旋转结束
    • rotatecancel: 旋转取消

# 事件对象

Name Value
type 事件的名称,例如 panstart
deltaX X 轴移动
deltaY Y 轴移动
deltaTime 自从第一次 input 的总时间,单位 ms
distance 移动距离
angle 角度
velocityX X 轴上的速度,单位为 px/ms
velocityY Y 轴上的速度,单位为 px/ms
velocity velocityX/velocityY 的最高值
direction 移动方向,匹配 DIRECTION 常量
offsetDirection 相对于起点的方向,匹配 DIRECTION 常量
scale 在多点触摸缩放时,只是触摸时值为 1
rotation 在多点触摸旋转时,只是触摸时值为 0
center 多点触摸的中心位置,或者只是单独的点
srcEvent 源事件对象,类型为 TouchEvent,MouseEvent 或 PointerEvent
target 收到该事件的目标
pointerType 主要点类型,可能是 touch,mouse,pen 或 kinect
eventType 事件类型,匹配 INPUT 常量
isFirst 第一个 input 时为 true
isFinal 最后一个 input 时为 true
pointers 所有点的数组,包括结束点(touchend,mouseup)
changedPointers 包含所有 new/moved/lost 的点
preventDefault 参考 srcEvent.preventDefault() 方法

# 使用注意事项

  1. 尽量避免垂直 pan/swipe
    垂直 pan 用于滚动页面,一些(旧)浏览器不会触发这个事件,所以 hammer.js 无法识别
  2. 删除 Windows Phone 上的突出显示
    Windows Phone 上的 IE10 和 IE11 在点击某个元素时会突出显示一个小点,添加这个 meta 来删除
    <meta name="msapplication-tap-highlight" content="no" />
  3. tap 后,click 也被触发了
    该点击事件也被称为 “幽灵点击”(ghost click)

# 案例

# 摇一摇

<!DOCTYPE html>
<html>
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
     <title>摇一摇抽奖</title>
    <style type="text/css">
        html,body{ width:100%; height:100%; background-color: #000; margin:0; overflow: hidden;}
       .tip{ position: absolute; bottom: 30px; left: 10px; color: #fff; font-family: '楷体'; text-align: center; right: 10px; height: 32px; line-height: 32px; background-color: rgba(255,255,255,.4); border-radius: 3px; } .tip.active{ -webkit-animation: jump 1.5s linear; animation: jump 1s linear; }
   </style>
</head>
<body>
    <div class="tip" id="tip"> </div>

    <script type="text/javascript">
        var lastX = null,
            lastY = null,
            lastZ = null;
        var threshold = 3; //灵敏度(值越小灵敏度越高)
        var timeout = 1000;
        var lastTime = null;
        var isShaking = !1;
        document.addEventListener('DOMContentLoaded', function (e) {
            ready();
        }, !1);
        /*脚本逻辑:
        *移动端JS脚本逻辑的实现,摇一摇的实现需借助html5新增的devicemotion事件,获取设备在位置和方向上的改变速度的相关信息。
        *devicemotion事件对象中有一个accelerationIncludingGravity属性,该属性包括:一个包含x、y 和z 属性的对象,在考虑z 轴自然重力加速度的情况下,告诉你在每个方向上的加速度。
        */
        function ready() {
            if (window.DeviceMotionEvent) {
                window.addEventListener('devicemotion', handler, !1);
                lastTime = new Date();
            } else {
                alert('你的浏览器不支持摇一摇功能.');
            }
        }
        function handler(e) {
            var current = e.accelerationIncludingGravity;
            var currentTime;
            var timeDifference;
            var deltaX = 0;
            var deltaY = 0;
            var deltaZ = 0;

            //记录上一次设备在x,y,z方向上的加速度
            if ((lastX === null) && (lastY === null) && (lastZ === null)) {
                lastX = current.x;
                lastY = current.y;
                lastZ = current.z;
                return;
            }

            //得到两次移动各个方向上的加速度绝对差距
            deltaX = Math.abs(lastX - current.x);
            deltaY = Math.abs(lastY - current.y);
            deltaZ = Math.abs(lastZ - current.z);
            //当差距大于设定的阀值并且时间间隔大于指定阀值时,触发摇一摇逻辑
            if (((deltaX > threshold) && (deltaY > threshold)) || ((deltaX > threshold) && (deltaZ > threshold)) || ((deltaY > threshold) && (deltaZ > threshold))) {
                currentTime = new Date;
                timeDifference = currentTime.getTime() - lastTime.getTime();
                //时间间隔
                if (timeDifference > timeout) {
                    //触发摇一摇事件
                    dealShake();
                    lastTime = new Date;
                }
            }

            lastX = current.x;
            lastY = current.y;
            lastZ = current.z;
        }

        function dealShake() {
            if (isShaking) return;
             isShaking = !0;

            document.getElementById("tip").innerHTML = "恭喜您,摇中:" + GetName();

            setTimeout(function () {
                isShaking = !1;
                document.getElementById("tip").innerHTML = " ";
            }, 1000);

        }
        function GetName() {
            var chars = ["一等奖", "二等奖", "三等奖", "四等奖", "五等奖"];
            return chars[GetRandom(0, chars.length - 1)];
        }
        function GetRandom(minValue, maxValue) {
            return Math.round(Math.random() * (maxValue - minValue)) + minValue;
        }
    </script>
</body>
</html>
Copied!