【Stage3D学习笔记续】山寨Starling(十一):Touch事件体系

时间:2024-01-08 16:48:26

我们的山寨Starling版本将会在这里停止更新了,主要还是由于时间比较有限,而且我们的山寨版本也很好的完成了他的任务“了解Starling的核心渲染”,接下来的Starling解析我们将会直接阅读Starling的源码来了解其它的内部运行方式。

Touch事件体系无疑是除了核心渲染外最重要的东西了,我们笔记中的Touch事件体系是基于Starling v1.1来解读的。

Touch事件体系简介:

在Starling的承继关系里是没有InteractiveObject类,因此所有的显示对象都默认具有交互功能。也就是说DisplayObject是定义了交互行为的。

我们看看主要的类的功能:

  • TouchEvent:Touch事件类,Starling中将原生的鼠标和触摸事件都统一封装成TouchEvent类,这样可以保证PC开发和移动端运行时的一致性,而不同于原生事件,TouchEvent的事件类型仅有一个“TouchEvent.TOUCH”。
  • Touch:一个Touch对象包含了在屏幕上的一个手指或鼠标的相关详细信息,如果是多点触控,则会存在多个Touch对象。
  • TouchPhase:因为TouchEvent只有一个事件类型“TOUCH”,那么我们要区分具体的情况(按下、移动和弹起等)就需要使用Touch对象的phase属性了,而TouchPhase则定义了phase属性存在的值。
  • TouchProcessor:Touch事件体系核心处理类,也是我们接下来要核心解读的类。
  • TouchMarker:内部使用,PC模拟多点触控时的显示对象。

Touch事件处理流程:

Starling中持有一个TouchProcessor类的实例“mTouchProcessor”,该类用来统一处理所有的Touch事件。

还需要注意的一点是,Touch事件会有"延迟",因为Starling捕获到原生Flash层的事件之后,不会马上派发给侦听Starling事件的对象,而是先发送内部的“队列”里面,然后根据帧频,进行集中处理。不过一般我们的帧频不会设置的太小,这个延迟对用户基本没有影响。

原生事件注册:

Starling类启动时会对Stage进行事件注册:

 public function Starling(xxx)
{
...
// register touch/mouse event handlers
for each (var touchEventType:String in touchEventTypes)
stage.addEventListener(touchEventType, onTouch, false, 0, true);
...
} private function get touchEventTypes():Array
{
return Mouse.supportsCursor || !multitouchEnabled ?
[ MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_UP ] :
[ TouchEvent.TOUCH_BEGIN, TouchEvent.TOUCH_MOVE, TouchEvent.TOUCH_END ];
}

我们可以看到会根据是否支持鼠标和是否开启多点触控来选择注册的事件类型,而所有事件的处理都交给onTouch方法。

onTouch方法处理:

 private function onTouch(event:Event):void
{
if (!mStarted) return; var globalX:Number;
var globalY:Number;
var touchID:int;
var phase:String; // figure out general touch properties
if (event is MouseEvent)
{
var mouseEvent:MouseEvent = event as MouseEvent;
globalX = mouseEvent.stageX;
globalY = mouseEvent.stageY;
touchID = 0; // MouseEvent.buttonDown returns true for both left and right button (AIR supports
// the right mouse button). We only want to react on the left button for now,
// so we have to save the state for the left button manually.
if (event.type == MouseEvent.MOUSE_DOWN) mLeftMouseDown = true;
else if (event.type == MouseEvent.MOUSE_UP) mLeftMouseDown = false;
}
else
{
var touchEvent:TouchEvent = event as TouchEvent;
globalX = touchEvent.stageX;
globalY = touchEvent.stageY;
touchID = touchEvent.touchPointID;
} // figure out touch phase
switch (event.type)
{
case TouchEvent.TOUCH_BEGIN: phase = TouchPhase.BEGAN; break;
case TouchEvent.TOUCH_MOVE: phase = TouchPhase.MOVED; break;
case TouchEvent.TOUCH_END: phase = TouchPhase.ENDED; break;
case MouseEvent.MOUSE_DOWN: phase = TouchPhase.BEGAN; break;
case MouseEvent.MOUSE_UP: phase = TouchPhase.ENDED; break;
case MouseEvent.MOUSE_MOVE:
phase = (mLeftMouseDown ? TouchPhase.MOVED : TouchPhase.HOVER); break;
} // move position into viewport bounds
globalX = mStage.stageWidth * (globalX - mViewPort.x) / mViewPort.width;
globalY = mStage.stageHeight * (globalY - mViewPort.y) / mViewPort.height; // enqueue touch in touch processor
mTouchProcessor.enqueue(touchID, phase, globalX, globalY);
}

onTouch方法中会区分原生鼠标事件和Touch事件,同时会获取到一些基础的信息(如全局坐标等)。

最终,会将收集到的核心信息调用mTouchProcessor.enqueue方法加入待处理队列。

Touch事件处理和抛出:

mTouchProcessor.enqueue方法收集每帧需要处理的事件,注意这个时候只是得到原生stage的一些鼠标或触摸的信息。

最终生成Starling的Touch事件是在Starling的onEnterFrame方法中调用的advanceTime方法中:

 private function advanceTime():void
{
var now:Number = getTimer() / 1000.0;
var passedTime:Number = now - mLastFrameTimestamp;
mLastFrameTimestamp = now; mStage.advanceTime(passedTime);
mJuggler.advanceTime(passedTime);
mTouchProcessor.advanceTime(passedTime);
}

我们发现在处理完毕stage和动画的advanceTime方法后,紧接着就是处理mTouchProcessor的advanceTime方法。注意Starling的所有Touch事件抛出都是在该方法中执行的。而这里执行完毕后就会执行渲染,所以如果我们在Touch事件中改变对象的状态是在本帧就能看到效果的。

mTouchProcessor的advanceTime方法会处理收集的所有Touch事件消息,清理旧的Tap标记(因为Tap操作需要较长的生命周期,所以额外处理),更新已有的Touch对象;将mQueue排队的事件参数进行处理,产生新的Touch对象(通过hitTest方法寻找到该全局坐标下的target目标),并且如果需要的话,让这个Touch事件所对应的对象来派发这个事件。

Touch事件的使用:

1.对需要进行Touch监听的对象进行事件侦听:

 sprite.addEventListener(TouchEvent.TOUCH, onTouch);

2.在onTouch方法中处理:

 private function onTouch(e:TouchEvent):void
{
//获取多个Touch对象,一个对象对应一个触碰,如果存在多个对象则表示是多点触碰
var touches:Vector.<Touch> = e.touches;
//只有一个触碰
if(touches.length == 1)
{
//...
}
//两个触碰
if(touches.length == 2)
{
//...
}
}

3.获取到的具体的Touch对象中包含需要的详细信息;

我这里只是简单的带过使用方法,更多详细的使用方法可以查看下面的电子书:

http://www.starlinglib.com/wiki/OutsideDocs.html