249 lines
7.4 KiB
JavaScript
249 lines
7.4 KiB
JavaScript
//>>built
|
|
define("dojox/geo/openlayers/TouchInteractionSupport", ["dojo/_base/kernel",
|
|
"dojo/_base/declare",
|
|
"dojo/_base/connect",
|
|
"dojo/_base/html",
|
|
"dojo/_base/lang",
|
|
"dojo/_base/event",
|
|
"dojo/_base/window"], function(dojo, declare, connect, html, lang, event, window){
|
|
|
|
return declare("dojox.geo.openlayers.TouchInteractionSupport", null, {
|
|
// summary:
|
|
// class to handle touch interactions on a OpenLayers.Map widget
|
|
// tags:
|
|
// private
|
|
|
|
_map : null,
|
|
_centerTouchLocation : null,
|
|
_touchMoveListener : null,
|
|
_touchEndListener : null,
|
|
_initialFingerSpacing : null,
|
|
_initialScale : null,
|
|
_tapCount : null,
|
|
_tapThreshold : null,
|
|
_lastTap : null,
|
|
|
|
constructor : function(/* OpenLayers.Map */map){
|
|
// summary:
|
|
// Constructs a new TouchInteractionSupport instance
|
|
// map: OpenLayers.Map
|
|
// the Map widget this class provides touch navigation for.
|
|
this._map = map;
|
|
this._centerTouchLocation = new OpenLayers.LonLat(0, 0);
|
|
|
|
var div = this._map.div;
|
|
|
|
// install touch listeners
|
|
connect.connect(div, "touchstart", this, this._touchStartHandler);
|
|
connect.connect(div, "touchmove", this, this._touchMoveHandler);
|
|
connect.connect(div, "touchend", this, this._touchEndHandler);
|
|
|
|
this._tapCount = 0;
|
|
this._lastTap = {
|
|
x : 0,
|
|
y : 0
|
|
};
|
|
this._tapThreshold = 100; // square distance in pixels
|
|
|
|
},
|
|
|
|
_getTouchBarycenter : function(touchEvent){
|
|
// summary:
|
|
// returns the midpoint of the two first fingers (or the first finger location if only one)
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// returns: dojox.gfx.Point
|
|
// the midpoint
|
|
// tags:
|
|
// private
|
|
var touches = touchEvent.touches;
|
|
var firstTouch = touches[0];
|
|
var secondTouch = null;
|
|
if (touches.length > 1) {
|
|
secondTouch = touches[1];
|
|
} else {
|
|
secondTouch = touches[0];
|
|
}
|
|
|
|
var marginBox = html.marginBox(this._map.div);
|
|
|
|
var middleX = (firstTouch.pageX + secondTouch.pageX) / 2.0 - marginBox.l;
|
|
var middleY = (firstTouch.pageY + secondTouch.pageY) / 2.0 - marginBox.t;
|
|
|
|
return {
|
|
x : middleX,
|
|
y : middleY
|
|
};
|
|
|
|
},
|
|
|
|
_getFingerSpacing : function(touchEvent){
|
|
// summary:
|
|
// computes the distance between the first two fingers
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// returns: float
|
|
// a distance. -1 if less that 2 fingers
|
|
// tags:
|
|
// private
|
|
var touches = touchEvent.touches;
|
|
var spacing = -1;
|
|
if (touches.length >= 2) {
|
|
var dx = (touches[1].pageX - touches[0].pageX);
|
|
var dy = (touches[1].pageY - touches[0].pageY);
|
|
spacing = Math.sqrt(dx * dx + dy * dy);
|
|
}
|
|
return spacing;
|
|
},
|
|
|
|
_isDoubleTap : function(touchEvent){
|
|
// summary:
|
|
// checks whether the specified touchStart event is a double tap
|
|
// (i.e. follows closely a previous touchStart at approximately the same location)
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// returns: boolean
|
|
// true if this event is considered a double tap
|
|
// tags:
|
|
// private
|
|
var isDoubleTap = false;
|
|
var touches = touchEvent.touches;
|
|
if ((this._tapCount > 0) && touches.length == 1) {
|
|
// test distance from last tap
|
|
var dx = (touches[0].pageX - this._lastTap.x);
|
|
var dy = (touches[0].pageY - this._lastTap.y);
|
|
var distance = dx * dx + dy * dy;
|
|
if (distance < this._tapThreshold) {
|
|
isDoubleTap = true;
|
|
} else {
|
|
this._tapCount = 0;
|
|
}
|
|
}
|
|
this._tapCount++;
|
|
this._lastTap.x = touches[0].pageX;
|
|
this._lastTap.y = touches[0].pageY;
|
|
setTimeout(lang.hitch(this, function(){
|
|
this._tapCount = 0;
|
|
}), 300);
|
|
|
|
return isDoubleTap;
|
|
},
|
|
|
|
_doubleTapHandler : function(touchEvent){
|
|
// summary:
|
|
// action performed on the map when a double tap was triggered
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// tags:
|
|
// private
|
|
// perform a basic 2x zoom on touch
|
|
var touches = touchEvent.touches;
|
|
var marginBox = html.marginBox(this._map.div);
|
|
var offX = touches[0].pageX - marginBox.l;
|
|
var offY = touches[0].pageY - marginBox.t;
|
|
// clicked map point before zooming
|
|
var mapPoint = this._map.getLonLatFromPixel(new OpenLayers.Pixel(offX, offY));
|
|
// zoom increment power
|
|
this._map.setCenter(new OpenLayers.LonLat(mapPoint.lon, mapPoint.lat), this._map.getZoom() + 1);
|
|
},
|
|
|
|
_touchStartHandler : function(touchEvent){
|
|
// summary:
|
|
// action performed on the map when a touch start was triggered
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// tags:
|
|
// private
|
|
event.stop(touchEvent);
|
|
|
|
// test double tap
|
|
if (this._isDoubleTap(touchEvent)) {
|
|
this._doubleTapHandler(touchEvent);
|
|
return;
|
|
}
|
|
|
|
// compute map midpoint between fingers
|
|
var middlePoint = this._getTouchBarycenter(touchEvent);
|
|
|
|
this._centerTouchLocation = this._map.getLonLatFromPixel(new OpenLayers.Pixel(middlePoint.x, middlePoint.y));
|
|
|
|
// store initial finger spacing to compute zoom later
|
|
this._initialFingerSpacing = this._getFingerSpacing(touchEvent);
|
|
|
|
// store initial map scale
|
|
this._initialScale = this._map.getScale();
|
|
|
|
// install touch move and up listeners (if not done by other fingers before)
|
|
if (!this._touchMoveListener)
|
|
this._touchMoveListener = connect.connect(window.global, "touchmove", this, this._touchMoveHandler);
|
|
if (!this._touchEndListener)
|
|
this._touchEndListener = connect.connect(window.global, "touchend", this, this._touchEndHandler);
|
|
|
|
},
|
|
|
|
_touchEndHandler : function(touchEvent){
|
|
// summary:
|
|
// action performed on the map when a touch end was triggered
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// tags:
|
|
// private
|
|
event.stop(touchEvent);
|
|
|
|
var touches = touchEvent.touches;
|
|
|
|
if (touches.length == 0) {
|
|
// disconnect listeners only when all fingers are up
|
|
if (this._touchMoveListener) {
|
|
connect.disconnect(this._touchMoveListener);
|
|
this._touchMoveListener = null;
|
|
}
|
|
if (this._touchEndListener) {
|
|
connect.disconnect(this._touchEndListener);
|
|
this._touchEndListener = null;
|
|
}
|
|
} else {
|
|
// recompute touch center
|
|
var middlePoint = this._getTouchBarycenter(touchEvent);
|
|
|
|
this._centerTouchLocation = this._map.getLonLatFromPixel(new OpenLayers.Pixel(middlePoint.x, middlePoint.y));
|
|
}
|
|
},
|
|
|
|
_touchMoveHandler : function(touchEvent){
|
|
// summary:
|
|
// action performed on the map when a touch move was triggered
|
|
// touchEvent: Event
|
|
// a touch event
|
|
// tags:
|
|
// private
|
|
|
|
// prevent browser interaction
|
|
event.stop(touchEvent);
|
|
|
|
var middlePoint = this._getTouchBarycenter(touchEvent);
|
|
|
|
// compute map offset
|
|
var mapPoint = this._map.getLonLatFromPixel(new OpenLayers.Pixel(middlePoint.x, middlePoint.y));
|
|
var mapOffsetLon = mapPoint.lon - this._centerTouchLocation.lon;
|
|
var mapOffsetLat = mapPoint.lat - this._centerTouchLocation.lat;
|
|
|
|
// compute scale factor
|
|
var scaleFactor = 1;
|
|
var touches = touchEvent.touches;
|
|
if (touches.length >= 2) {
|
|
var fingerSpacing = this._getFingerSpacing(touchEvent);
|
|
scaleFactor = fingerSpacing / this._initialFingerSpacing;
|
|
// weird openlayer bug : setting several times the same scale value lead to visual zoom...
|
|
this._map.zoomToScale(this._initialScale / scaleFactor);
|
|
}
|
|
|
|
// adjust map center on barycenter
|
|
var currentMapCenter = this._map.getCenter();
|
|
this._map.setCenter(new OpenLayers.LonLat(currentMapCenter.lon - mapOffsetLon, currentMapCenter.lat
|
|
- mapOffsetLat));
|
|
|
|
}
|
|
});
|
|
});
|