iPhone的出现让手势操作大为流行,也使得手势编程成为开发人员的挑战。 拟物设计也把手势编程纳入在内,大概也想制定一个在交互模型标准。现阶段因为MD还在预发布阶段,因此还只实现了单点手势(一个指头),可是已经有足够的东西值得学习,无论对我们应用还是自己设计手势编程都是大有裨益。

Angular Material有两个手划控件mdSwipeLeftmdSwipeRight,然而真正的代码支持却不在这两个控件的定义中,而是在核心代码中,文件位置src\core\services\gesture\gesture.js这里也就是我们深入研究手势实现的地方。

基本屏幕事件

做过界面的人都熟悉mousedown, mouseup, mousemove等事件,很多后台函数多与这些事件绑定,从而能够与用户交互。但是这些事件都有些单薄而僵硬,手势事件却更友好和人性化,这也是其大受欢迎的根本原因。

手势事件不是空中楼阁,它们本身是需要这些基本事件的支持,这些基本屏幕事件也就成为了手势模型的一个组成部分,成为最底的一层。

这些事件首先被划分为三类,说是三类,理解成三个事件更为恰当,它们与手指与屏幕的交互一一对应:开始事件就是手指按下屏幕;移动事件就是手指在屏幕移动;结束事件就是手指离开屏幕。非常简单而直观。

从下面MD对这三类事件的定义,我们也可以看到每类事件中的变体大都与设备的不同有关而不是真正的不同事件,如鼠标的按下,和手指的按下。这也是我上面说的把它们理解为三个事件更为恰当。

  • START_EVENTS =>'mousedown touchstart pointerdown';
  • MOVE_EVENTS => 'mousemove touchmove pointermove';
  • END_EVENTS => 'mouseup mouseleave touchend touchcancel pointerup pointercancel';

手势归纳

基本事件都是瞬间事件,不存在延时和逻辑判断,按下就是按下,松开就是松开;这也是称之为基本事件的原因。

而手势却恰恰相反,

  • 手势是综合事件,如滑动手势,直观的感觉就是手指按下快速向左(右)滑动,并同时松开手指,这整个过程完成才是一个滑动手势。
  • 手势还有逻辑判断,还是滑动手势,不仅仅要在以上的全过程之后才激发,手指的还要超过一定的速度才能算是滑动手势。

因此,可以把手势看作在基本事件之上的一个封装,在MD的实现也是用GestureHandler的函数还侦听基本事件然后作出综合处理。

侦听

这里是MD绑定基本事件的代码:

angular.element(document)
.on(START_EVENTS, gestureStart)
.on(MOVE_EVENTS, gestureMove)
.on(END_EVENTS, gestureEnd)

MD移动事件的侦听处理函数:

function gestureMove(ev) {
if (!pointer || !typesMatch(ev, pointer)) return;
updatePointerState(ev, pointer);
runHandlers('move', ev);
}

其它两个(开始和结束事件)都与此类似,只不过有更多的处理过程。这个因为简单,可以用来好好分析关键过程。我们可以看到,这个侦听函数的关键一步就是调用处理器(runHandler)。这个函数内部并不复杂,只是简单的遍历预存处理器,然后调用该处理器定义的对应的基本事件处理器。这个处理器就是手势处理器,它会分析归纳基本事件当条件满足时触发手势事件。

手势处理器$$MdGestureHandler

MD用工厂(factory)的方式定义了手势处理器的模板(或者可以理解为基类帮助理解),这个factory名称就是$$MdGestureHandler,为了便于理解,我们把它分解成三部分来看。

基本屏幕事件处理

第一部分:4个方法,分别与三类基本屏幕事件对应(cancel是辅助方法),也是用来分别处理三类屏幕事件的,上面的runHandler就是调用的源头。

start: function(ev, pointer) {
if (this.state.isRunning) return;
var parentTarget = this.getNearestParent(ev.target);
var parentTargetOptions = parentTarget && parentTarget.$mdGesture[this.name] || {}; this.state = {
isRunning: true,
options: angular.extend({}, this.options, parentTargetOptions),
registeredParent: parentTarget
};
this.onStart(ev, pointer);
},
move: function(ev, pointer) {
if (!this.state.isRunning) return;
this.onMove(ev, pointer);
},
end: function(ev, pointer) {
if (!this.state.isRunning) return;
this.onEnd(ev, pointer);
this.state.isRunning = false;
},
cancel: function(ev, pointer) {
this.onCancel(ev, pointer);
this.state = {};
},
优化的屏幕事件

第二部分:4个内部事件,也是基本与以上4个方法对应,并在4个方法中适当的时机触发,可以看作是对原始基本事件的梳理之后的重新抛出。 你如果创建自己的手势处理器,要做的也就是重载这4个事件。从以下代码我们也可以看到,MD为每一个事件给出了空实现(`angular.noop'),目的就是为了让自定义处理器自己重载实现。

onStart: angular.noop,
onMove: angular.noop,
onEnd: angular.noop,
onCancel: angular.noop,
手势的触发

第三部分:也是最后最关键的一个方法,手势事件的触发dispatchEvent。自定义的手势处理器最终都是要调用这个方法来触发手势事件。大部分触发时机都在onEnd中,当是不是必须的,要根据你具体的手势的含义来定。

dispatchEvent的实现:

dispatchEvent: dispatchEvent,
...
/*
* NOTE: dispatchEvent is very performance sensitive.
*/
function dispatchEvent(srcEvent, eventType, eventPointer, /*original DOMEvent */ev) {
eventPointer = eventPointer || pointer;
var eventObj; if (eventType === 'click') {
eventObj = document.createEvent('MouseEvents');
eventObj.initMouseEvent(
'click', true, true, window, ev.detail,
ev.screenX, ev.screenY, ev.clientX, ev.clientY,
ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey,
ev.button, ev.relatedTarget || null
); } else {
eventObj = document.createEvent('CustomEvent');
eventObj.initCustomEvent(eventType, true, true, {});
}
eventObj.$material = true;
eventObj.pointer = eventPointer;
eventObj.srcEvent = srcEvent;
eventPointer.target.dispatchEvent(eventObj);
}

手势实例解析

手势内部实现过程虽然较为复杂,以上的流程解析也是为了更好的理解从而有个直观的感觉。到了每一个手势的实现时,真正用到的却不算多,主要就是那4个优化的事件onStart, onMove, onEnd, onCancel和一个触发的方法'dispatchEvent`。我们来看看一些手势实例,亲身感受一下,良好建模以后的手势实现。

滑动手势 - Swipe
屏幕事件 触发条件 触发事件
[无]
按下
移动
移动
移动
移动
松开 超过最低速度和位移 $md.swiperight
[无]
拖动手势 - Drag
屏幕事件 触发条件 触发事件
[无]
按下
移动
移动 当前触点与起点位移超过阀值 $md.dragstart
移动 $md.drag
移动 $md.drag
松开 $md.dragend
[无]

手势模型和Angular Material的实现的更多相关文章

  1. Material使用11 核心模块和共享模块、 如何使用@angular/material

    1 创建项目 1.1 版本说明 1.2 创建模块 1.2.1 核心模块 该模块只加载一次,主要存放一些核心的组件及服务 ng g m core 1.2.1.1 创建一些核心组件 页眉组件:header ...

  2. Angular Material & Hello World

    前言 Angular Material(下称Material)的组件样式至少是可以满足一般的个人开发需求(我真是毫无设计天赋),也是Angular官方推荐的组件.我们通过用这个UI库来快速实现自己的i ...

  3. 从零3D基础入门XNA 4.0(2)——模型和BasicEffect

    [题外话] 上一篇文章介绍了3D开发基础与XNA开发程序的整体结构,以及使用Model类的Draw方法将模型绘制到屏幕上.本文接着上一篇文章继续,介绍XNA中模型的结构.BasicEffect的使用以 ...

  4. NOSQL数据模型和CAP原理

    NOSQL数据模型和CAP原理 http://blog.sina.com.cn/s/blog_7800d9210100t33v.html 我本来一直觉得NoSQL其实很容易理解的,我本身也已经对NoS ...

  5. 网络知识学习1---(基础知识:ISO/OSI七层模型和TCP/IP四层模型)

    以下的内容和之后的几篇博客只是比较初级的介绍,想要深入学习的话建议自己钻研<TCP/IP详解 卷1:协议> 1.ISO/OSI七层模型    下四层是为数据传输服务的,物理层是真正的传输数 ...

  6. 黑马-----内存模型和volatile详解

    黑马程序员:Java培训.Android培训.iOS培训..Net培训 JAVA线程-内存模型和volatile详解 一.单核内存模型 1.程序运行时,将临时数据存放到Cache中 2.将CPU计算所 ...

  7. 系统间通信(5)——IO通信模型和JAVA实践 下篇

    7.异步IO 上面两篇文章中,我们分别讲解了阻塞式同步IO.非阻塞式同步IO.多路复用IO 这三种IO模型,以及JAVA对于这三种IO模型的支持.重点说明了IO模型是由操作系统提供支持,且这三种IO模 ...

  8. 复杂领域的Cynefin模型和Stacey模型

    最近好奇“复杂系统”,收集了点资料,本文关于Cynefin模型和Stacey模型.图文转自互联网后稍做修改. Cynefin模型提供一个从因果关系复杂情度来分析当前情况而作决定的框架,提出有五个领域: ...

  9. 异步IO模型和Overlapped结构

    .NET中的 Overlapped 类 异步IO模型和Overlapped结构(http://blog.itpub.net/25897606/viewspace-705867/) 数据结构 OVERL ...

随机推荐

  1. Oracle中 Package与Package body的介绍

    1.Oracle Package的作用: 可以简化应用设计.提高应用性能.实现信息隐藏.子程序重载 2.ORACLE中的function   .package.package   bodies.pro ...

  2. 使用css3中calc()进行自适应布局

    calc()能做什么? calc()可以通过计算得到元素的宽度或者高度,让我们很容易进行自适应布局. 你可以为一个div元素,使用百分比.em.px和rem单位值计算出其宽度或者高度,比如说“widt ...

  3. localstorage,sessionstorage使用

    今天看了一下HTML5,也算是简单的学习一下吧,HTML5 提供了两种在客户端存储数据的新方法:localstorage,sessionstorage. localStorage - 没有时间限制的数 ...

  4. 在Eclipse上操作Hive-0.13.1-JDBC端口

    fesh个人实践,欢迎经验交流!本文Blog地址:http://www.cnblogs.com/fesh/p/3877740.html 完成<在Hadoop-2.2.0集群上安装 Hive-0. ...

  5. 百度API使用--javascript api进行多点定位

    使用百度地图提供的javascript api,给定多点的经纬度坐标,在百度地图上 显示这些坐标点. 其中包括各个点自适应地图显示,自定义坐标点的图标,以及各个点之间添加折线. 实现的效果如下图: 具 ...

  6. Java 容器:Collection 初探之 List

    1 ///: JavaBasic//com.cnblogs.pattywgm.day1//CollectionTest.java 2 3 package com.cnblogs.pattywgm.da ...

  7. 完成了第一个java

    这是我的第一个java程序,求水仙花数2016-04-26  22:39:09

  8. 在unity3d中使用opencv

    1.首先下载opencv2.4.10,解压缩后放在合适的地方,然后根据自己的电脑(32位或64位)选择X86或X64,我的是32位,将“opencv存放路径\build\x86\vc12\bin”加入 ...

  9. CentOS 6.3 安装 phpmyadmin

    安装phpMyAdminphpMyAdmin是一个网络接口,通过它可以管理你的MySQL数据库.首先,我们使CentOS系统RPMForge软件库的phpMyAdmin,而不是官方的CentOS 6. ...

  10. Eclipse报错:Setting property 'source' to 'org.eclipse.jst.jee.server:test1' did no

    最近把Eclipse的maven插件从m2eclipse更新到m2e后出了一些莫名其妙的的问题.今天又出了一个,就是Eclipse新建的Maven Web project在tomcat里启动后报错,具 ...