02_核心概念--05_手势

来源:互联网 发布:英语语义学 知乎 编辑:程序博客网 时间:2024/05/17 23:17

除了标准的DOM事件,元素还能触发合成的手势事件。从浏览器的角度来看,有三个主要的类型,包括指针,触摸和鼠标事件-开始,移动和结束。

Event

Touch

Pointer

Mouse

Start

touchstart

pointerdown

mousedown

Move

touchmove

pointermove

mousemove

Stop

touchend

pointerup

mouseup

通过解决这些事件的顺序和时间,框架能够合成更复杂的事件,例如 drag, swipe,longpress, pinch, rotate和tap。ExtJS程序能够监听手势事件,如同监听其它事件一样,例如:

Ext.get('myElement').on('longpress',handlerFunction);

通过合成上述三种类型的事件,鼠标,指针和触摸,Ext JS能够让任何手势对任意输入作出反应。这意味着,不仅所有的手势事件可以触发,而且所有通过鼠标完成的单点手势(如tap, swipe等)也能够被触发。最终生成一个手势系统,该系统可以在各种输入类型的设备之间无缝地正常工作。

Ext JS现在支持如下的手势:

Gesture

Events

Tap

tap, tapcancel

DoubleTap

singletap, doubletap

LongPress

longpress

Drag

dragstart, drag, dragend, dragcancel

Swipe

swipestart, swipe, swipecancel

Pinch

pinchstart, pinch, pinchend, pinchcancel

Rotate

rotatestart, rotate, rotateend, rotatecancel

EdgeSwipe

edgeswipe, edgeswipestart, edgeswipeend, edgeswipecancel

指针类型

在一些特殊情况下,应用需要监听由某种特定的输入设备(鼠标或触摸屏)触发的手势,而忽略其它类型的设备。Ext JS在事件对象上提供了一个pointerType属性来判断引起事件的输入设备的类型。

el.on('drag', function(e) {    if (e.pointerType === 'touch') {        // only handle touch-drag, and ignore mouse-drag    }});

事件传播

事件在框架中的传播跟DOM事件在浏览器中的传播十分相似。不同点在于框架使用了一个事件代理模型,目的是为了支持手势识别。这意味着事件在一个单独的传播阶段被传递到DOM元素 ,这个传播阶段是在事件已经在DOM级别完成传播时发生的。


原生传播

事件监听器,是被直接附加到一个DOM元素上。当浏览器通过DOM结构传播事件时,事件监听器将会被调用。虽然理解原生传播的机制也是十分有用的,但在ExtJS中,直接附加的DOM监听器通常会因如下原因避免:

*直接附加的监听器的触发顺序与其它监听器不一致。

*从直接附加的监听器里调用stopPropagation会阻止手势识别的发生,也会阻止相关的合成传播。

然而,在一些情况下,直接附加一个监听器到一个DOM元素上是十分必要的,例如,当使用Ext JS与其它框架一起解决问题时。这可以使用delegated事件选项来完成。

el.on({    mousedown: function() {        Ext.Msg.alert('mousedown fired - native capture phase');    },    // careful when using delegated: false! this can have unintended side effects    delegated: false,    // use the capture option to listen in the capture phase    capture: true});

DOM级别的事件传播发生在两个阶段。第一个阶段是捕获阶段 ,在这个阶段,事件被派发到从DOM顶层开始的每个元素,从window对象开始,所有的经过事件传播途径的元素,直到事件的目标元素。捕获阶段过后,冒泡阶段就发生了。在冒泡阶段,事件从目标元素开始被传递,然后到它的祖先元素(例如父元素),直到它达到结构的最顶层。默认情况下,监听器是在冒泡传播阶段被触发的,但相关的部分监听器可以通过capture事件选项来设置到捕获阶段。

手势识别

在原生的事件冒泡到达窗体对象后,Ext JS执行手势识别。然后系统会合成一个或多个手势事件,这些事件一定会通过DOM传播。

合成传播

在手势识别阶段完成后,框架将派发原生的DOM事件,例如,mousedown或者touchmove,同时也会派发其它的手势事件,例如drag或者swipe,这些手势事件可以被看作是DOM事件的结果。跟原生传播一样,合成传播也发生在两个阶段,捕获阶段和冒泡阶段。

合成传播在框架中对所有事件是默认的事件传播方式,在使用ExtJS的程序中也是被推荐使用的传播方式。使用合成传播方式能保证事件与其它事件按照合适的顺序触发,能够避免因为某些原因导致的传播停止而引起的异常。开发者不需要做任何事情来激活合成传播-下面的监听器使用了合成传播:

el.on('mousedown', function() {    Ext.Msg.alert('mousedown fired - synthetic bubble phase');});

当然,使用capture选项可以将监听置于捕获阶段:

el.on({    mousedown: function() {        Ext.Msg.alert('mousedown fired - synthetic capture phase');    },    capture: true});

停止传播

在原生传播和合成传播过程中的任何一点,传播都可以被停止,不管是在捕获阶段还是在冒泡阶段。


这阻止了事件被派发到任何还没有收到事件的元素上,同时也阻止了悬停的捕获和冒泡阶段的继续执行。

例如,当一个事件被派发到一个元素时,阻止该传播会阻止事件冒泡到父元素:
parentEl.on('mousedown', function() {    // never fires because child element in bubble phase stops propagation of mousedown    Ext.Msg.alert('mousedown fired on child');});el.on('mousedown', function(e) {    Ext.Msg.alert('mousedown fired on child');    // immediately stop propagating the event any further    e.stopPropagation();});

停止在捕获阶段的传播会取消整个冒泡过程。

el.on({    mousedown: function(e) {        Ext.Msg.alert('mousedown - capture phase');        // stopping propagation during the capture phase causes the entire        // bubble phase to be skipped        e.stopPropagation();    },    capture: true});el.on('mousedown', function() {    // never fires because propagation of mousedown event was stopped prior to    // bubble phase    Ext.Msg.alert('mousedown - bubble phase');});

手势传播

当一个手势事件被识别时,它会通过DOM层级随着原生事件传播,而手势事件是通过这些原生事件合成的。在一些情况下,多个手势的识别可以同时发生。当这种情况发生时,手势传播会集中在一套单独的捕获或冒泡阶段。

例如,假设一个DOM层级结构里面,A元素包含B元素,B元素又包含C元素。假设一个touchmove事件在最里面的C元素上触发了,那么这个touchmove事件符合了drag和swipe手势触发的条件。




一旦识别到两个事件,drag和swipe发生,手势发布者发布touchmove,dragstart和
swipestart事件,并把这三个事件传送到层级结构的每个元素。


声明手势

有时,在相同层级结构中的元素可以监听冲突的手势。在上面的示例中,想像一下,元素A正在监听swipe事件,同时它的子组件,元素B,在监听drag事件。当元素B处理dragstart事件时,它或许想要阻止当前的手势被swipe事件打断,因为元素A的swipe处理函数或许会与元素B的drag处理函数一起被触发。要想达到这个目的,必须通过调用事件对象上的claimGetture方法来声明drag手势 。

el.on('dragstart', function(e) {    e.claimGesture();});

一旦claimGesture在dragstart事件对象上被调用,在该事件接下来的时间,该手势就会被识别为一个drag手势(直到用户抬起手指或释放鼠标按钮)。所有与swipe手势相关的事件(swipestart,swipe, swipeend)将会停止触发。此外,因为swipe事件被取消,一个swipecancel事件将会在传播层级结构中被传递到每个元素。



浏览器对手势的处理(触摸动作)

浏览器会自动处理特定的触摸手势,执行默认的行为作为反馈,例如滚动或缩放。这些行为被叫做'touchactions'(触摸动作)。浏览器实现的经典的触摸动作如下:

Gesture

Touch Action

drag (horizontal)

horizontal scroll

drag (vertical)

vertical scroll

pinch

zoom

doubletap

zoom

那些为这些手势实现了它们自己的处理函数的Ext JS应用程序,当这些手势事件被处理时,或许需要禁用一个或多个浏览器默认的触摸处理行为。例如,一个可拖动的元素在它被手动时最希望阻止它的容器的滚动。这个可以通过设置元素的touchAction来实现。

// instruct the browser not to scroll while "el" is being draggedel.setTouchAction({    panX: false,    panY: false});

在CSS的touch-action属性出现并支持相同的值后,Ext JS里的触摸行为的概念被模式化。在后台,框架在浏览器支持使用CSS的touch-action的地方使用它(在那些实现了Pointer Events的浏览器上),在Touch Events的浏览器上呢,它也会模仿相同的行为。

CSS Name

Ext JS Name

pan-x

panX

pan-y

panY

pinch-zoom

pinchZoom

double-tap-zoom

doubleTapZoom

当处理组件时,要避免的直接在它们的元素上调用setTouchAction,而是使用组件的touchAction配置项来代替。例如,下面的面板的主元素不允许浏览器垂直滚动,而且它的body元素也不允许pinch-to-zoom(通过捏操作来缩放)或者垂直滚动,因为它通过DOM继承了它的父节点的touchAction。


Ext.create('Ext.panel.Panel', {    touchAction: {        panY: false,        body: {            pinchZoom: false        }    },    listeners: {        drag: function(e) {            // handle drag on the panel's main element        },        pinch: {            element: 'body',            fn: function(e) {                // handle pinch on the panel's body element            }        }    }});

从长按到拖动

在触摸设备上,有时很难判断一个触摸操作是还应该滚动或拖动一个对象。通常通过时间临界值来判断用户的操作意图。在一个元素上按的时间够长并且手势转换为一个拖操作而不是滚动。现在这项判断可以使用新的可用的startDrag方法,该方法在longpress事件函数里执行。

el.on('longpress',function(e){

    e.startDrag();

});

在指针事件浏览器(IE和Edge),当元素被拖动时,你必须在元素上设置相应的触摸行为以阻止浏览器滚动。

el.setTouchAction({

    panX: false,

    panY: false

});

在触摸事件浏览器(Chrome和Safari)中,为了阻止滚动,配置元素的触摸操作行为也是没有必要的。相反,你可以在拖动事件对象上调用preventDefault方法来阻止滚动的发生。

el.on('drag',function(e){

    e.preventDefault();

});

当preventDefault方法在指针事件型浏览器上阻止滚动不起作用时,在触摸事件型浏览器上,它的优点在于在它能决定是否在运行时允许滚动,而不是提前声明一个触摸行为。对于只需要支持触摸事件型浏览器的应用来说,这将是一个很有用的技巧,然而,对于跨平台应用来说,你必须使用touchActin来阻止滚动。

 

视口缩放

这篇指南假定视窗缩放在你的应用中是可用的。比较经典的做法是添加如下的meta标签到你的html页面:

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

应用如果想禁用视窗绽放应该用如下的meta标签来替换上面的标签:

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

我们推荐一起应用视窗缩放功能,因为它对于视力受损者提高了访问性。


原创粉丝点击