Merge pull request #164 from bbinet/kinetic
Add support for kinetic effect while dragging
This commit is contained in:
@@ -80,6 +80,13 @@ ol.interaction.Drag.prototype.handleDragEnd = goog.nullFunction;
|
|||||||
ol.interaction.Drag.prototype.handleDragStart = goog.functions.FALSE;
|
ol.interaction.Drag.prototype.handleDragStart = goog.functions.FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
ol.interaction.Drag.prototype.handleDown = goog.nullFunction;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +98,10 @@ ol.interaction.Drag.prototype.handleMapBrowserEvent =
|
|||||||
}
|
}
|
||||||
var view = map.getView();
|
var view = map.getView();
|
||||||
var browserEvent = mapBrowserEvent.browserEvent;
|
var browserEvent = mapBrowserEvent.browserEvent;
|
||||||
|
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DOWN) {
|
||||||
|
goog.asserts.assert(browserEvent instanceof goog.events.BrowserEvent);
|
||||||
|
this.handleDown(mapBrowserEvent);
|
||||||
|
}
|
||||||
if (this.dragging_) {
|
if (this.dragging_) {
|
||||||
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DRAG) {
|
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DRAG) {
|
||||||
goog.asserts.assert(browserEvent instanceof goog.events.BrowserEvent);
|
goog.asserts.assert(browserEvent instanceof goog.events.BrowserEvent);
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ goog.require('ol.interaction.Drag');
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @extends {ol.interaction.Drag}
|
* @extends {ol.interaction.Drag}
|
||||||
* @param {ol.interaction.ConditionType} condition Condition.
|
* @param {ol.interaction.ConditionType} condition Condition.
|
||||||
|
* @param {ol.Kinetic=} opt_kinetic Kinetic object.
|
||||||
*/
|
*/
|
||||||
ol.interaction.DragPan = function(condition) {
|
ol.interaction.DragPan = function(condition, opt_kinetic) {
|
||||||
|
|
||||||
goog.base(this);
|
goog.base(this);
|
||||||
|
|
||||||
@@ -27,6 +28,18 @@ ol.interaction.DragPan = function(condition) {
|
|||||||
*/
|
*/
|
||||||
this.condition_ = condition;
|
this.condition_ = condition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.Kinetic|undefined}
|
||||||
|
*/
|
||||||
|
this.kinetic_ = opt_kinetic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {?ol.PreRenderFunction}
|
||||||
|
*/
|
||||||
|
this.kineticPreRenderFn_ = null;
|
||||||
|
|
||||||
};
|
};
|
||||||
goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
|
goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
|
||||||
|
|
||||||
@@ -35,6 +48,9 @@ goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
|
ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
|
||||||
|
if (this.kinetic_) {
|
||||||
|
this.kinetic_.update(mapBrowserEvent.browserEvent);
|
||||||
|
}
|
||||||
var map = mapBrowserEvent.map;
|
var map = mapBrowserEvent.map;
|
||||||
// FIXME works for View2D only
|
// FIXME works for View2D only
|
||||||
var view = map.getView();
|
var view = map.getView();
|
||||||
@@ -55,9 +71,27 @@ ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.interaction.DragPan.prototype.handleDragEnd = function(mapBrowserEvent) {
|
ol.interaction.DragPan.prototype.handleDragEnd = function(mapBrowserEvent) {
|
||||||
|
|
||||||
|
// FIXME works for View2D only
|
||||||
|
|
||||||
var map = mapBrowserEvent.map;
|
var map = mapBrowserEvent.map;
|
||||||
map.requestRenderFrame();
|
var view = map.getView();
|
||||||
map.getView().setHint(ol.ViewHint.PANNING, -1);
|
view.setHint(ol.ViewHint.PANNING, -1);
|
||||||
|
|
||||||
|
if (this.kinetic_ && this.kinetic_.end()) {
|
||||||
|
var distance = this.kinetic_.getDistance();
|
||||||
|
var angle = this.kinetic_.getAngle();
|
||||||
|
var center = view.getCenter();
|
||||||
|
this.kineticPreRenderFn_ = this.kinetic_.createPanFrom(center);
|
||||||
|
map.addPreRenderFunction(this.kineticPreRenderFn_);
|
||||||
|
|
||||||
|
var centerpx = map.getPixelFromCoordinate(center);
|
||||||
|
var destpx = new ol.Pixel(
|
||||||
|
centerpx.x - distance * Math.cos(angle),
|
||||||
|
centerpx.y - distance * Math.sin(angle));
|
||||||
|
var dest = map.getCoordinateFromPixel(destpx);
|
||||||
|
view.setCenter(dest);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +101,9 @@ ol.interaction.DragPan.prototype.handleDragEnd = function(mapBrowserEvent) {
|
|||||||
ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
|
ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
|
||||||
var browserEvent = mapBrowserEvent.browserEvent;
|
var browserEvent = mapBrowserEvent.browserEvent;
|
||||||
if (this.condition_(browserEvent)) {
|
if (this.condition_(browserEvent)) {
|
||||||
|
if (this.kinetic_) {
|
||||||
|
this.kinetic_.begin(browserEvent);
|
||||||
|
}
|
||||||
var map = mapBrowserEvent.map;
|
var map = mapBrowserEvent.map;
|
||||||
map.requestRenderFrame();
|
map.requestRenderFrame();
|
||||||
map.getView().setHint(ol.ViewHint.PANNING, 1);
|
map.getView().setHint(ol.ViewHint.PANNING, 1);
|
||||||
@@ -75,3 +112,21 @@ ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.interaction.DragPan.prototype.handleDown = function(mapBrowserEvent) {
|
||||||
|
var map = mapBrowserEvent.map;
|
||||||
|
// FIXME works for View2D only
|
||||||
|
var view = map.getView();
|
||||||
|
goog.asserts.assert(view instanceof ol.View2D);
|
||||||
|
goog.asserts.assert(!goog.isNull(mapBrowserEvent.frameState));
|
||||||
|
if (!goog.isNull(this.kineticPreRenderFn_) &&
|
||||||
|
map.removePreRenderFunction(this.kineticPreRenderFn_)) {
|
||||||
|
map.requestRenderFrame();
|
||||||
|
view.setCenter(mapBrowserEvent.frameState.view2DState.center);
|
||||||
|
this.kineticPreRenderFn_ = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
|
||||||
|
goog.provide('ol.Kinetic');
|
||||||
|
|
||||||
|
goog.require('goog.array');
|
||||||
|
goog.require('ol.Pixel');
|
||||||
|
goog.require('ol.animation');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{x: number,
|
||||||
|
* y: number,
|
||||||
|
* t: number}}
|
||||||
|
*/
|
||||||
|
ol.KineticPoint;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {number} decay Rate of decay (must be negative).
|
||||||
|
* @param {number} minVelocity Minimum velocity (pixels/millisecond).
|
||||||
|
* @param {number} delay Delay to consider to calculate the kinetic
|
||||||
|
* initial values (milliseconds).
|
||||||
|
*/
|
||||||
|
ol.Kinetic = function(decay, minVelocity, delay) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.decay_ = decay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.minVelocity_ = minVelocity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.delay_ = delay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Array.<ol.KineticPoint>}
|
||||||
|
*/
|
||||||
|
this.points_ = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.angle_ = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.initialVelocity_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {goog.events.BrowserEvent} browserEvent Browser event.
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.begin = function(browserEvent) {
|
||||||
|
this.points_.length = 0;
|
||||||
|
this.angle_ = 0;
|
||||||
|
this.initialVelocity_ = 0;
|
||||||
|
this.update(browserEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {goog.events.BrowserEvent} browserEvent Browser event.
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.update = function(browserEvent) {
|
||||||
|
this.points_.push({
|
||||||
|
x: browserEvent.clientX,
|
||||||
|
y: browserEvent.clientY,
|
||||||
|
t: goog.now()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} Whether we should do kinetic animation.
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.end = function() {
|
||||||
|
var now = goog.now();
|
||||||
|
var lastIndex = this.points_.length - 1;
|
||||||
|
var firstIndex = lastIndex - 1;
|
||||||
|
while (firstIndex >= 0 && this.points_[firstIndex].t > now - this.delay_) {
|
||||||
|
firstIndex--;
|
||||||
|
}
|
||||||
|
if (firstIndex >= 0) {
|
||||||
|
var first = this.points_[firstIndex];
|
||||||
|
var last = this.points_[lastIndex];
|
||||||
|
var dx = last.x - first.x;
|
||||||
|
var dy = last.y - first.y;
|
||||||
|
this.angle_ = Math.atan2(dy, dx);
|
||||||
|
this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / (last.t - first.t);
|
||||||
|
return this.initialVelocity_ > this.minVelocity_;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Coordinate} source Source coordinate for the animation.
|
||||||
|
* @return {ol.PreRenderFunction} Pre-render function for kinetic animation.
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.createPanFrom = function(source) {
|
||||||
|
var decay = this.decay_;
|
||||||
|
var initialVelocity = this.initialVelocity_;
|
||||||
|
var minVelocity = this.minVelocity_;
|
||||||
|
var duration = this.getDuration_();
|
||||||
|
var easingFunction = function(t) {
|
||||||
|
return initialVelocity * (Math.exp((decay * t) * duration) - 1) /
|
||||||
|
(minVelocity - initialVelocity);
|
||||||
|
};
|
||||||
|
return ol.animation.createPanFrom({
|
||||||
|
source: source,
|
||||||
|
duration: duration,
|
||||||
|
easing: easingFunction
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @return {number} Duration of animation (milliseconds).
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getDuration_ = function() {
|
||||||
|
return Math.log(this.minVelocity_ / this.initialVelocity_) / this.decay_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} Total distance travelled (pixels).
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getDistance = function() {
|
||||||
|
return (this.minVelocity_ - this.initialVelocity_) / this.decay_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} Angle of the kinetic panning animation (radians).
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getAngle = function() {
|
||||||
|
return this.angle_;
|
||||||
|
};
|
||||||
+13
-1
@@ -29,6 +29,7 @@ goog.require('ol.Color');
|
|||||||
goog.require('ol.Coordinate');
|
goog.require('ol.Coordinate');
|
||||||
goog.require('ol.Extent');
|
goog.require('ol.Extent');
|
||||||
goog.require('ol.FrameState');
|
goog.require('ol.FrameState');
|
||||||
|
goog.require('ol.Kinetic');
|
||||||
goog.require('ol.MapBrowserEvent');
|
goog.require('ol.MapBrowserEvent');
|
||||||
goog.require('ol.Object');
|
goog.require('ol.Object');
|
||||||
goog.require('ol.Pixel');
|
goog.require('ol.Pixel');
|
||||||
@@ -325,6 +326,15 @@ ol.Map.prototype.addPreRenderFunctions = function(preRenderFunctions) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.PreRenderFunction} preRenderFunction Pre-render function.
|
||||||
|
* @return {boolean} Whether the preRenderFunction has been found and removed.
|
||||||
|
*/
|
||||||
|
ol.Map.prototype.removePreRenderFunction = function(preRenderFunction) {
|
||||||
|
return goog.array.remove(this.preRenderFunctions_, preRenderFunction);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
@@ -532,6 +542,7 @@ ol.Map.prototype.handleControlsRemove_ = function(collectionEvent) {
|
|||||||
* @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle.
|
* @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle.
|
||||||
*/
|
*/
|
||||||
ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
|
ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
|
||||||
|
mapBrowserEvent.frameState = this.frameState_;
|
||||||
var interactions = this.getInteractions();
|
var interactions = this.getInteractions();
|
||||||
var interactionsArray = /** @type {Array.<ol.interaction.Interaction>} */
|
var interactionsArray = /** @type {Array.<ol.interaction.Interaction>} */
|
||||||
(interactions.getArray());
|
(interactions.getArray());
|
||||||
@@ -984,7 +995,8 @@ ol.Map.createInteractions_ = function(mapOptions) {
|
|||||||
mapOptions.dragPan : true;
|
mapOptions.dragPan : true;
|
||||||
if (dragPan) {
|
if (dragPan) {
|
||||||
interactions.push(
|
interactions.push(
|
||||||
new ol.interaction.DragPan(ol.interaction.condition.noModifierKeys));
|
new ol.interaction.DragPan(ol.interaction.condition.noModifierKeys,
|
||||||
|
new ol.Kinetic(-0.005, 0.05, 100)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyboard = goog.isDef(mapOptions.keyboard) ?
|
var keyboard = goog.isDef(mapOptions.keyboard) ?
|
||||||
|
|||||||
@@ -21,10 +21,11 @@ goog.require('ol.Pixel');
|
|||||||
* @param {string} type Event type.
|
* @param {string} type Event type.
|
||||||
* @param {ol.Map} map Map.
|
* @param {ol.Map} map Map.
|
||||||
* @param {goog.events.BrowserEvent} browserEvent Browser event.
|
* @param {goog.events.BrowserEvent} browserEvent Browser event.
|
||||||
|
* @param {?ol.FrameState=} opt_frameState Frame state.
|
||||||
*/
|
*/
|
||||||
ol.MapBrowserEvent = function(type, map, browserEvent) {
|
ol.MapBrowserEvent = function(type, map, browserEvent, opt_frameState) {
|
||||||
|
|
||||||
goog.base(this, type, map);
|
goog.base(this, type, map, opt_frameState);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {goog.events.BrowserEvent}
|
* @type {goog.events.BrowserEvent}
|
||||||
@@ -230,6 +231,9 @@ ol.MapBrowserEventHandler.prototype.handleUp_ = function(browserEvent) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
ol.MapBrowserEventHandler.prototype.handleDown_ = function(browserEvent) {
|
ol.MapBrowserEventHandler.prototype.handleDown_ = function(browserEvent) {
|
||||||
|
var newEvent = new ol.MapBrowserEvent(
|
||||||
|
ol.MapBrowserEvent.EventType.DOWN, this.map_, browserEvent);
|
||||||
|
this.dispatchEvent(newEvent);
|
||||||
if (!this.previous_) {
|
if (!this.previous_) {
|
||||||
this.touchEnableBrowserEvent_(browserEvent);
|
this.touchEnableBrowserEvent_(browserEvent);
|
||||||
this.down_ = browserEvent;
|
this.down_ = browserEvent;
|
||||||
@@ -306,5 +310,6 @@ ol.MapBrowserEvent.EventType = {
|
|||||||
DBLCLICK: goog.events.EventType.DBLCLICK,
|
DBLCLICK: goog.events.EventType.DBLCLICK,
|
||||||
DRAGSTART: 'dragstart',
|
DRAGSTART: 'dragstart',
|
||||||
DRAG: 'drag',
|
DRAG: 'drag',
|
||||||
DRAGEND: 'dragend'
|
DRAGEND: 'dragend',
|
||||||
|
DOWN: 'down'
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user