Since the correct handling of the divergent browser specific multitouch
implementations is a difficult and recurring task we decided to encapsulate
all related handlers for TouchEvent
(WebKit, Mozilla) and
PointerEvent
(IE, Edge, Chrome) in
a single delegate pattern.
The main differences are that PointerEvent
are fired for each
touch point, whereas the TouchEvent
collects multiple
TouchPoints
into a single event. The basic PointMap and Interaction
classes unify this behavior by collecting all contact points regardless
of their original mouse, touch, or pointer events.
The touch and pointer positions are collected in PointMaps which provide
access to the positions via stringified touch and pointer ids. For mouse events the
special id "mouse" is used. PointMaps can be cloned and pretty printed. In addition
they provide auxiliary methods like mean
and farthests
which can be used to simplify the computation of gestures. In general
mean
can be used to compute the "center of interaction", i.e. the
best guess of the anchor point for rotation and scaling operations.
If more than two touch points are involved it may be best to look for the pair of points which are farthest away from each other. These points will represent the fingers farthest away from each other, a more simple substitute for 3, 4 or 5 touch points. Here we add a third point to our example touches and test whether the maximal distant points are found:
Interaction objects extend the idea of mapping touch ids to points to multiple target objects. Each touch id is mapped not only to the changing points of this touch but also to the object that has been hit by the starting touch point. This object is the target of the interaction and remains for the whole duration of the multitouch gesture.
The delegator registers all needed TouchEvent
,
PointerEvent
, and MouseEvent
handlers on a provided DOM elememt for a given target object, ensures that the
events are captured by the target and boils the event handling down to simple
onStart
, onMove
, onEnd
events.
Let's look at an example of an InteractionDelegate and a target object that
implements the IInteractionTarget
interface. Typically you setup
the delegator in the constructor of the class that uses the interation.
We can now check whether the promised interface methods are implemented by the class:
If we define an InteractionTarget that violates the IInteractionTarget interface we get an error. The following example of an interaction target uses an InteractionDelegate but does not implement the necessary methods:
Often we need to assign UI elements to touch and pointer events. This is supported by a special InteractionMapper delegate. A InteractionMapper maps events to specific parts of a container interaction target. The InteractionTarget must implement a findTarget method that returns an object implementing the IInteractionTarget interface.
If the InteractionTarget also implements a mapPositionToPoint
method this
is used to map the points to the local coordinate space of the the target.
This makes it easier to lookup elements and relate events to local
positions.
Let's see an example. A graph that uses an InterationMapper
for it´s child
objects:
Now we simulate a sequence of onStart, onMove, onEnd
events by calling
the registered event handlers programmatically. Note that the defined
event handlers log their calls.
Drag & Drop is a common interaction pattern. This behavior can be accomplished by a class that implements IInteractionMapperTarget as well as IInteractionTarget. You can grab the blue circle with touches or mouse and drag it around.
Multitouch-Events (simultaneous events) in browsers cannot be used by default. Even libraries like jQuery do not fix this problem. The static method "on" of the InteractionMapper allows simultaneous events and thus multitouch. The following events (and their specializations) can be used in addition to the default browser events: tap, doubletap, press, pan, swipe, pinch and rotate. See http://hammerjs.github.io for more details.