[OpenLayers-Commits] r11220 - in trunk/openlayers: examples lib
lib/OpenLayers lib/OpenLayers/Control tests
commits-20090109 at openlayers.org
commits-20090109 at openlayers.org
Tue Feb 22 04:59:21 EST 2011
Author: erilem
Date: 2011-02-22 01:59:21 -0800 (Tue, 22 Feb 2011)
New Revision: 11220
Added:
trunk/openlayers/examples/kinetic.html
trunk/openlayers/lib/OpenLayers/Kinetic.js
trunk/openlayers/tests/Kinetic.html
Modified:
trunk/openlayers/examples/mobile.js
trunk/openlayers/lib/OpenLayers.js
trunk/openlayers/lib/OpenLayers/Control/DragPan.js
trunk/openlayers/tests/list-tests.html
Log:
kinetic/momemtum dragging support, p=camptocamp, r=me (closes #2999)
Added: trunk/openlayers/examples/kinetic.html
===================================================================
--- trunk/openlayers/examples/kinetic.html (rev 0)
+++ trunk/openlayers/examples/kinetic.html 2011-02-22 09:59:21 UTC (rev 11220)
@@ -0,0 +1,81 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>OpenLayers Kinetic Dragging Example</title>
+ <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+ <link rel="stylesheet" href="style.css" type="text/css" />
+ <style type="text/css">
+ #map {
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+ <script src="../lib/OpenLayers.js"></script>
+ <script type="text/javascript">
+ var map, layer;
+ function init(){
+ map = new OpenLayers.Map({
+ div: "map",
+ resolutions: [0.087890625, 0.0439453125, 0.02197265625, 0.010986328125],
+ panDuration: 100,
+ controls: [
+ new OpenLayers.Control.Navigation(
+ {dragPanOptions: {enableKinetic: true}}
+ )
+ ]
+ });
+ layer = new OpenLayers.Layer.TileCache("TileCache Layer",
+ ["http://c0.tilecache.osgeo.org/wms-c/cache/",
+ "http://c1.tilecache.osgeo.org/wms-c/cache/",
+ "http://c2.tilecache.osgeo.org/wms-c/cache/",
+ "http://c3.tilecache.osgeo.org/wms-c/cache/",
+ "http://c4.tilecache.osgeo.org/wms-c/cache/"],
+ "basic",
+ {
+ serverResolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625,
+ 0.0439453125, 0.02197265625, 0.010986328125,
+ 0.0054931640625, 0.00274658203125, 0.001373291015625,
+ 0.0006866455078125, 0.00034332275390625, 0.000171661376953125,
+ 0.0000858306884765625, 0.00004291534423828125, 0.000021457672119140625],
+ buffer: 4
+ }
+ );
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ }
+ </script>
+ </head>
+ <body onload="init()">
+ <h1 id="title">Kinetic Dragging Example</h1>
+
+ <div id="tags">
+ kinetic, dragging
+ </div>
+
+ <p id="shortdesc">
+ Demonstrates Kinetic Dragging.
+ </p>
+
+ <div id="map" class="smallmap"></div>
+
+ <div id="docs">
+
+ <p>
+ OpenLayers Kinetic Dragging inspired from <a href="http://www.tile5.org">Tile5</a>, and
+ <a href="http://code.google.com/p/kineticscrolling/">kineticscrolling</a> for Google Maps API V3.
+ </p>
+
+ <p>
+
+ As shown in this example Kinetic Dragging is enabled by setting
+ <code>enableKinetic</code> to true in the config object provided to the
+ <code>Control.DragPan</code> constructor. When using
+ <code>Control.Navigation</code> or <code>Control.TouchNavigation</code>
+ providing options to the underlying <code>Control.DragPan</code>
+ instance is done through the <code>dragPanOptions</code> config
+ property.
+
+ </p>
+
+ </div>
+ </body>
+</html>
Modified: trunk/openlayers/examples/mobile.js
===================================================================
--- trunk/openlayers/examples/mobile.js 2011-02-22 09:49:37 UTC (rev 11219)
+++ trunk/openlayers/examples/mobile.js 2011-02-22 09:59:21 UTC (rev 11220)
@@ -19,7 +19,7 @@
-20037508.34, -20037508.34, 20037508.34, 20037508.34
),
controls: [
- new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.Navigation({dragPanOptions: {enableKinetic: true}}),
new OpenLayers.Control.Attribution(),
new OpenLayers.Control.DrawFeature(
vector, OpenLayers.Handler.Point, {id: "point-control"}
Modified: trunk/openlayers/lib/OpenLayers/Control/DragPan.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Control/DragPan.js 2011-02-22 09:49:37 UTC (rev 11219)
+++ trunk/openlayers/lib/OpenLayers/Control/DragPan.js 2011-02-22 09:59:21 UTC (rev 11220)
@@ -43,16 +43,48 @@
* mouse cursor leaves the map viewport. Default is false.
*/
documentDrag: false,
-
+
/**
+ * Property: kinetic
+ * {OpenLayers.Kinetic} The OpenLayers.Kinetic object.
+ */
+ kinetic: null,
+
+ /**
+ * APIProperty: enableKinetic
+ * {Boolean} Set this option to enable "kinetic dragging". Can be
+ * set to true or to an object. If set to an object this
+ * object will be passed to the {<OpenLayers.Kinetic>}
+ * constructor. Defaults to false.
+ */
+ enableKinetic: false,
+
+ /**
+ * APIProperty: kineticInterval
+ * {Integer} Interval in milliseconds between 2 steps in the "kinetic
+ * scrolling". Applies only if enableKinetic is set. Defaults
+ * to 10 milliseconds.
+ */
+ kineticInterval: 10,
+
+
+ /**
* Method: draw
* Creates a Drag handler, using <panMap> and
* <panMapDone> as callbacks.
*/
draw: function() {
+ if(this.enableKinetic) {
+ var config = {interval: this.kineticInterval};
+ if(typeof this.enableKinetic === "object") {
+ config = OpenLayers.Util.extend(config, this.enableKinetic);
+ }
+ this.kinetic = new OpenLayers.Kinetic(config);
+ }
this.handler = new OpenLayers.Handler.Drag(this, {
"move": this.panMap,
- "done": this.panMapDone
+ "done": this.panMapDone,
+ "down": this.panMapStart
}, {
interval: this.interval,
documentDrag: this.documentDrag
@@ -61,17 +93,29 @@
},
/**
+ * Method: panMapStart
+ */
+ panMapStart: function() {
+ if(this.kinetic) {
+ this.kinetic.begin();
+ }
+ },
+
+ /**
* Method: panMap
*
* Parameters:
* xy - {<OpenLayers.Pixel>} Pixel of the mouse position
*/
panMap: function(xy) {
+ if(this.kinetic) {
+ this.kinetic.update(xy);
+ }
this.panned = true;
this.map.pan(
this.handler.last.x - xy.x,
this.handler.last.y - xy.y,
- {dragging: this.handler.dragging, animate: false}
+ {dragging: true, animate: false}
);
},
@@ -85,7 +129,21 @@
*/
panMapDone: function(xy) {
if(this.panned) {
- this.panMap(xy);
+ var res = null;
+ if (this.kinetic) {
+ res = this.kinetic.end(xy);
+ }
+ this.map.pan(
+ this.handler.last.x - xy.x,
+ this.handler.last.y - xy.y,
+ {dragging: !!res, animate: false}
+ );
+ if (res) {
+ var self = this;
+ this.kinetic.move(res, function(x, y, end) {
+ self.map.pan(x, y, {dragging: !end, animate: false});
+ });
+ }
this.panned = false;
}
},
Added: trunk/openlayers/lib/OpenLayers/Kinetic.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Kinetic.js (rev 0)
+++ trunk/openlayers/lib/OpenLayers/Kinetic.js 2011-02-22 09:59:21 UTC (rev 11220)
@@ -0,0 +1,169 @@
+OpenLayers.Kinetic = OpenLayers.Class({
+
+ /**
+ * Property: threshold
+ * In most cases changing the threshold isn't needed.
+ * In px/ms, default to 0.
+ */
+ threshold: 0,
+
+ /**
+ * Property: interval
+ * {Integer} Interval in milliseconds between 2 steps in the "kinetic
+ * dragging". Defaults to 10 milliseconds.
+ */
+ interval: 10,
+
+ /**
+ * Property: deceleration
+ * {Float} the deseleration in px/ms², default to 0.0035.
+ */
+ deceleration: 0.0035,
+
+ /**
+ * Property: nbPoints
+ * {Integer} the number of points we use to calculate the kinetic
+ * initial values.
+ */
+ nbPoints: 100,
+
+ /**
+ * Property: delay
+ * {Float} time to consider to calculate the kinetic initial values.
+ * In ms, default to 200.
+ */
+ delay: 200,
+
+ /**
+ * Property: points
+ * List of points use to calculate the kinetic initial values.
+ */
+ points: undefined,
+
+ /**
+ * Property: timerId
+ * ID of the timer.
+ */
+ timerId: undefined,
+
+ /**
+ * Constructor: OpenLayers.Kinetic
+ *
+ * Parameters:
+ * options - {Object}
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: begin
+ *
+ * Begins the dragging.
+ */
+ begin: function() {
+ clearInterval(this.timerId);
+ this.timerId = undefined;
+ this.points = [];
+ },
+
+ /**
+ * Method: update
+ *
+ * Updates during the dragging.
+ */
+ update: function(xy) {
+ this.points.unshift({xy: xy, tick: new Date().getTime()});
+ if (this.points.length > this.nbPoints) {
+ this.points.pop();
+ }
+ },
+
+ /**
+ * Method: end
+ *
+ * Ends the dragging, start the kinetic.
+ */
+ end: function(xy) {
+ var last, now = new Date().getTime();
+ for (var i = 0, l = this.points.length, point; i < l; i++) {
+ point = this.points[i];
+ if (now - point.tick > this.delay) {
+ break;
+ }
+ last = point;
+ }
+ if (!last) {
+ return;
+ }
+ var time = new Date().getTime() - last.tick;
+ var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +
+ Math.pow(xy.y - last.xy.y, 2));
+ var speed = dist / time;
+ if (speed == 0 || speed < this.threshold) {
+ return;
+ }
+ var theta = Math.asin((xy.y - last.xy.y) / dist);
+ if (last.xy.x <= xy.x) {
+ theta = Math.PI - theta;
+ }
+ return {speed: speed, theta: theta};
+ },
+
+ /**
+ * Method: move
+ *
+ * Launch the kinetic move pan.
+ *
+ * Parameters:
+ * info - {Object}
+ * callback - arguments x, y (values to pan), end (is the last point)
+ */
+ move: function(info, callback) {
+ var v0 = info.speed;
+ var fx = Math.cos(info.theta);
+ var fy = -Math.sin(info.theta);
+
+ var time = 0;
+ var initialTime = new Date().getTime();
+
+ var lastX = 0;
+ var lastY = 0;
+
+ var timerCallback = function() {
+ if (this.timerId == null) {
+ return;
+ }
+
+ time += this.interval;
+ var realTime = new Date().getTime() - initialTime;
+ var t = (time + realTime) / 2.0;
+
+ var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;
+ var x = p * fx;
+ var y = p * fy;
+
+ var args = {};
+ args.end = false;
+ var v = -this.deceleration * t + v0;
+
+ if (v <= 0) {
+ clearInterval(this.timerId);
+ this.timerId = null;
+ args.end = true;
+ }
+
+ args.x = x - lastX;
+ args.y = y - lastY;
+ lastX = x;
+ lastY = y;
+ callback(args.x, args.y, args.end);
+ };
+
+ this.timerId = window.setInterval(
+ OpenLayers.Function.bind(timerCallback, this),
+ this.interval);
+ },
+
+ CLASS_NAME: "OpenLayers.Kinetic"
+});
Modified: trunk/openlayers/lib/OpenLayers.js
===================================================================
--- trunk/openlayers/lib/OpenLayers.js 2011-02-22 09:49:37 UTC (rev 11219)
+++ trunk/openlayers/lib/OpenLayers.js 2011-02-22 09:59:21 UTC (rev 11220)
@@ -105,6 +105,7 @@
"OpenLayers/BaseTypes/Size.js",
"OpenLayers/Console.js",
"OpenLayers/Tween.js",
+ "OpenLayers/Kinetic.js",
"Rico/Corner.js",
"Rico/Color.js",
"OpenLayers/Ajax.js",
Added: trunk/openlayers/tests/Kinetic.html
===================================================================
--- trunk/openlayers/tests/Kinetic.html (rev 0)
+++ trunk/openlayers/tests/Kinetic.html 2011-02-22 09:59:21 UTC (rev 11220)
@@ -0,0 +1,130 @@
+<html>
+<head>
+ <script src="OLLoader.js"></script>
+ <script type="text/javascript">
+
+ function test_Kinetic (t) {
+ t.plan(17);
+ var finish = false;
+ var results = {
+ 110: {x: -2.7, y: -3.6, end: false},
+ 120: {x: -2.1, y: -2.8, end: false},
+ 130: {x: -1.5, y: -2.0, end: false},
+ 140: {x: -0.9, y: -1.2, end: false},
+ 150: {x: -0.3, y: -0.4, end: true}
+ };
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var originalSetInterval = window.setInterval;
+ window.setInterval = function(callback, interval) {
+ while (!finish) {
+ var time = new Date().getTime();
+ Date.prototype.getTime = function() { return time+interval };
+ callback();
+ }
+ };
+
+ var kinetic = new OpenLayers.Kinetic({
+ deceleration: 0.01
+ });
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:30, y:40});
+
+ t.eq(measure.speed, 0.5, "correct speed");
+ t.eq(measure.theta, Math.PI - Math.atan(40/30), "correct angle");
+
+ // fake timer id
+ kinetic.timerId = 0;
+ kinetic.move(measure, function(x, y, end) {
+ var result = results[new Date().getTime()];
+ t.eq(Math.round(x * 1000) / 1000, result.x, "correct x");
+ t.eq(Math.round(y * 1000) / 1000, result.y, "correct y");
+ t.eq(end, result.end, "correct end");
+ finish = end;
+ });
+
+ Date.prototype.getTime = originalGetTime;
+ window.setInterval = originalSetInterval;
+ }
+
+ function test_Angle (t) {
+ t.plan(8);
+ var results = [
+ {speed: 0.5, theta: Math.round((Math.PI - Math.atan(40/30)) * 1000000) / 1000000},
+ {speed: 0.5, theta: Math.round((Math.PI + Math.atan(40/30)) * 1000000) / 1000000},
+ {speed: 0.5, theta: Math.round((- Math.atan(40/30)) * 1000000) / 1000000},
+ {speed: 0.5, theta: Math.round((Math.atan(40/30)) * 1000000) / 1000000}
+ ];
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:30, y:40});
+
+ t.eq(measure.speed, results[0].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[0].theta, "correct angle");
+
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:30, y:-40});
+
+ t.eq(measure.speed, results[1].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[1].theta, "correct angle");
+
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:-30, y:-40});
+
+ t.eq(measure.speed, results[2].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[2].theta, "correct angle");
+
+ var originalGetTime = Date.prototype.getTime;
+ Date.prototype.getTime = function() { return 0 };
+
+ var kinetic = new OpenLayers.Kinetic();
+ kinetic.begin();
+ kinetic.update({x:0, y:0});
+
+ Date.prototype.getTime = function() { return 100 };
+ var measure = kinetic.end({x:-30, y:40});
+
+ t.eq(measure.speed, results[3].speed, "correct speed");
+ t.eq(Math.round(measure.theta * 1000000) / 1000000,
+ results[3].theta, "correct angle");
+
+ Date.prototype.getTime = originalGetTime;
+ }
+ </script>
+</head>
+<body>
+ <div id="map" style="width: 600px; height: 300px;"/>
+ <div style="display: none;"><div id="invisimap"></div></div>
+</body>
+</html>
Modified: trunk/openlayers/tests/list-tests.html
===================================================================
--- trunk/openlayers/tests/list-tests.html 2011-02-22 09:49:37 UTC (rev 11219)
+++ trunk/openlayers/tests/list-tests.html 2011-02-22 09:59:21 UTC (rev 11220)
@@ -208,5 +208,6 @@
<li>Tile/Image/IFrame.html</li>
<li>Tile/WFS.html</li>
<li>Tween.html</li>
+ <li>Kinetic.html</li>
<li>Util.html</li>
</ul>
More information about the Commits
mailing list