Skip to content

Commit a2a391d

Browse files
committed
Fixed problems with touch events on a scrollable page
1 parent 8d0da09 commit a2a391d

File tree

9 files changed

+266
-43
lines changed

9 files changed

+266
-43
lines changed

examples/mobile-scroll.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>OpenLayers Mobile</title>
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
7+
<meta name="apple-mobile-web-app-capable" content="yes">
8+
<link rel="stylesheet" href="../theme/default/style.mobile.css" type="text/css">
9+
<script src="../lib/OpenLayers.js?mobile"></script>
10+
<script src="mobile-scroll.js"></script>
11+
<style>
12+
html, body {
13+
margin : 0;
14+
padding : 0;
15+
height : 100%;
16+
width : 100%;
17+
}
18+
@media only screen and (max-width: 600px) {
19+
html, body {
20+
height : 117%;
21+
}
22+
}
23+
#map {
24+
width : 100%;
25+
position : absolute;
26+
height : 70%;
27+
top : 500px;
28+
}
29+
.olControlAttribution {
30+
position : absolute;
31+
font-size : 10px;
32+
bottom : 0 !important;
33+
right : 0 !important;
34+
background : rgba(0,0,0,0.1);
35+
font-family : Arial;
36+
padding : 2px 4px;
37+
border-radius : 5px 0 0 0;
38+
}
39+
40+
</style>
41+
</head>
42+
<body>
43+
<h1 id="title">Scroll down to the map</h1>
44+
<div id="tags">
45+
mobile
46+
</div>
47+
<p id="shortdesc">
48+
A basic full-screen map for mobile devices.
49+
</p>
50+
<div id="map"></div>
51+
<script>
52+
init();
53+
</script>
54+
</body>
55+
</html>

examples/mobile-scroll.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// initialize map when page ready
2+
var map;
3+
4+
var init = function () {
5+
// create map
6+
console.log("Initialize MAP!!!");
7+
map = new OpenLayers.Map({
8+
div: "map",
9+
theme: null,
10+
controls: [
11+
new OpenLayers.Control.Attribution(),
12+
new OpenLayers.Control.TouchNavigation({
13+
dragPanOptions: {
14+
enableKinetic: true
15+
},
16+
defaultClick: addPin
17+
}),
18+
new OpenLayers.Control.Zoom()
19+
],
20+
layers: [
21+
new OpenLayers.Layer.OSM("OpenStreetMap", null, {
22+
transitionEffect: 'resize'
23+
})
24+
],
25+
center: new OpenLayers.LonLat(742000, 5861000),
26+
zoom: 3
27+
});
28+
var markers = new OpenLayers.Layer.Markers( "Markers" );
29+
map.addLayer(markers);
30+
31+
var size = new OpenLayers.Size(21,25);
32+
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
33+
var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png',size,offset);
34+
35+
36+
function addPin(e) {
37+
var pointMapProj = map.getLonLatFromViewPortPx(e.xy);
38+
markers.addMarker(new OpenLayers.Marker(pointMapProj,icon));
39+
}
40+
};

lib/OpenLayers/Events.js

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ OpenLayers.Events = OpenLayers.Class({
903903
var num = touches.length;
904904
var touch;
905905
for (var i=0; i<num; ++i) {
906-
touch = touches[i];
906+
touch = this.getTouchClientXY(touches[i]);
907907
x += touch.clientX;
908908
y += touch.clientY;
909909
}
@@ -915,7 +915,47 @@ OpenLayers.Events = OpenLayers.Class({
915915
}
916916
this.triggerEvent(type, evt);
917917
},
918-
918+
919+
/**
920+
* Method: getTouchClientXY
921+
* WebKit has a few bugs for clientX/clientY. This method detects them
922+
* and calculate the correct values.
923+
*
924+
* Parameters:
925+
* evt - {Event or Touch} Either the event object or a single touch object
926+
*
927+
* Returns:
928+
* {Object} An object with only clientX and clientY properties with the
929+
* calculated values.
930+
*/
931+
getTouchClientXY: function (evt) {
932+
var win = window._mockWin || window,
933+
winPageX = win.pageXOffset,
934+
winPageY = win.pageYOffset,
935+
x = evt.clientX,
936+
y = evt.clientY;
937+
938+
if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||
939+
evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {
940+
// iOS4 include scroll offset in clientX/Y
941+
x = x - winPageX;
942+
y = y - winPageY;
943+
} else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) {
944+
// Some Android browsers have totally bogus values for clientX/Y
945+
// when scrolling/zooming a page
946+
x = evt.pageX - winPageX;
947+
y = evt.pageY - winPageY;
948+
}
949+
950+
evt.olClientX = x;
951+
evt.olClientY = y;
952+
953+
return {
954+
clientX: x,
955+
clientY: y
956+
};
957+
},
958+
919959
/**
920960
* APIMethod: clearMouseCache
921961
* Clear cached data about the mouse position. This should be called any
@@ -925,17 +965,7 @@ OpenLayers.Events = OpenLayers.Class({
925965
clearMouseCache: function() {
926966
this.element.scrolls = null;
927967
this.element.lefttop = null;
928-
// OpenLayers.Util.pagePosition needs to use
929-
// element.getBoundingClientRect to correctly calculate the offsets
930-
// for the iPhone, but once the page is scrolled, getBoundingClientRect
931-
// returns incorrect offsets. So our best bet is to not invalidate the
932-
// offsets once we have them, and hope that the page was not scrolled
933-
// when we did the initial calculation.
934-
var body = document.body;
935-
if (body && !((body.scrollTop != 0 || body.scrollLeft != 0) &&
936-
navigator.userAgent.match(/iPhone/i))) {
937-
this.element.offsets = null;
938-
}
968+
this.element.offsets = null;
939969
},
940970

941971
/**
@@ -959,8 +989,8 @@ OpenLayers.Events = OpenLayers.Class({
959989
if (!this.element.scrolls) {
960990
var viewportElement = OpenLayers.Util.getViewportElement();
961991
this.element.scrolls = [
962-
viewportElement.scrollLeft,
963-
viewportElement.scrollTop
992+
window.pageXOffset || viewportElement.scrollLeft,
993+
window.pageYOffset || viewportElement.scrollTop
964994
];
965995
}
966996

lib/OpenLayers/Handler/Click.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,8 +495,8 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
495495
for (var i=0; i<len; i++) {
496496
touch = evt.touches[i];
497497
touches[i] = {
498-
clientX: touch.clientX,
499-
clientY: touch.clientY
498+
clientX: touch.olClientX,
499+
clientY: touch.olClientY
500500
};
501501
}
502502
}

lib/OpenLayers/Handler/Pinch.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
9797
};
9898
this.callback("start", [evt, this.start]);
9999
propagate = !this.stopDown;
100+
} else if (this.started) {
101+
// Some webkit versions send fake single-touch events during
102+
// multitouch, which cause the drag handler to trigger
103+
return false;
100104
} else {
101105
this.started = false;
102106
this.start = null;
@@ -125,6 +129,11 @@ OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
125129
this.last = current;
126130
// prevent document dragging
127131
OpenLayers.Event.stop(evt);
132+
return false;
133+
} else if (this.started) {
134+
// Some webkit versions send fake single-touch events during
135+
// multitouch, which cause the drag handler to trigger
136+
return false;
128137
}
129138
return true;
130139
},
@@ -140,12 +149,13 @@ OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
140149
* {Boolean} Let the event propagate.
141150
*/
142151
touchend: function(evt) {
143-
if (this.started) {
152+
if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
144153
this.started = false;
145154
this.pinching = false;
146155
this.callback("done", [evt, this.start, this.last]);
147156
this.start = null;
148157
this.last = null;
158+
return false;
149159
}
150160
return true;
151161
},
@@ -199,8 +209,8 @@ OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
199209
var t0 = touches[0];
200210
var t1 = touches[1];
201211
return Math.sqrt(
202-
Math.pow(t0.clientX - t1.clientX, 2) +
203-
Math.pow(t0.clientY - t1.clientY, 2)
212+
Math.pow(t0.olClientX - t1.olClientX, 2) +
213+
Math.pow(t0.olClientY - t1.olClientY, 2)
204214
);
205215
},
206216

lib/OpenLayers/Util.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,9 +1195,9 @@ OpenLayers.Util.pagePosition = function(forElement) {
11951195

11961196
if (forElement.getBoundingClientRect) { // IE
11971197
box = forElement.getBoundingClientRect();
1198-
var scrollTop = viewportElement.scrollTop;
1199-
var scrollLeft = viewportElement.scrollLeft;
1200-
1198+
var scrollTop = window.pageYOffset || viewportElement.scrollTop;
1199+
var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
1200+
12011201
pos[0] = box.left + scrollLeft;
12021202
pos[1] = box.top + scrollTop;
12031203

tests/Events.html

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@
300300
}
301301

302302
function test_Events_handleBrowserEvent(t) {
303-
t.plan(2);
303+
t.plan(8);
304304
var events = new OpenLayers.Events({}, null);
305305
events.on({'sometouchevent': function() {}});
306306

@@ -312,6 +312,57 @@
312312
events.handleBrowserEvent(evt);
313313
t.eq(evt.clientX, 1.5, "evt.clientX value is correct");
314314
t.eq(evt.clientY, 1.5, "evt.clientY value is correct");
315+
316+
// test bug where clientX/clientY includes scroll offset
317+
window._mockWin = {
318+
pageXOffset: 10,
319+
pageYOffset: 20
320+
};
321+
evt = {type: 'sometouchevent',
322+
touches: [{
323+
clientX: 11,
324+
clientY: 21,
325+
pageX: 0,
326+
pageY: 0
327+
}]
328+
};
329+
events.handleBrowserEvent(evt);
330+
t.eq(evt.clientX, 1, "evt.clientX value is correct");
331+
t.eq(evt.clientY, 1, "evt.clientY value is correct");
332+
333+
334+
// test bug where clientX/clientY have negative values
335+
evt = {
336+
type: 'sometouchevent',
337+
touches: [{
338+
clientX: -412,
339+
clientY: -1005,
340+
pageX: 11,
341+
pageY: 21
342+
}]
343+
};
344+
events.handleBrowserEvent(evt);
345+
t.eq(evt.clientX, 1, "evt.clientX value is correct");
346+
t.eq(evt.clientY, 1, "evt.clientY value is correct");
347+
348+
window._mockWin = {
349+
pageXOffset: 11,
350+
pageYOffset: 299
351+
};
352+
evt = {
353+
type: 'sometouchevent',
354+
touches: [{
355+
clientX: 223,
356+
clientY: 119,
357+
pageX: 242,
358+
pageY: 623
359+
}]
360+
};
361+
events.handleBrowserEvent(evt);
362+
t.eq(evt.clientX, 231, "evt.clientX value is correct");
363+
t.eq(evt.clientY, 324, "evt.clientY value is correct");
364+
365+
window._mockWin = undefined;
315366
}
316367

317368
function test_Events_attachToElement(t) {

0 commit comments

Comments
 (0)