Add support for kinetic effect while dragging.
Only dragpan interaction is supported for now.
The kinetic effect use the maths below (thx @twpayne):
Rate of decay (number, must be negative) = α
Minimum velocity (pixels/millisecond) = v_min
Initial velocity (pixels/millisecond) = v₀
Velocity at time t (pixels/millisecond) =
α⋅t
v₀⋅ℯ
Distance travelled at time t (pixels) =
⎛ α⋅t ⎞
v₀⋅⎝ℯ - 1⎠
─────────────
α
Duration of animation (milliseconds) =
⎛v_min⎞
log⎜─────⎟
⎝ v₀ ⎠
──────────
α
Total distance travelled (pixels) =
-v₀ + v_min
───────────
α
Easing function =
⎛ α⋅duration⋅t ⎞
v₀⋅⎝ℯ - 1⎠
──────────────────────
-v₀ + v_min
This commit is contained in:
@@ -7,6 +7,7 @@ goog.require('ol.Coordinate');
|
|||||||
goog.require('ol.MapBrowserEvent');
|
goog.require('ol.MapBrowserEvent');
|
||||||
goog.require('ol.View2D');
|
goog.require('ol.View2D');
|
||||||
goog.require('ol.ViewHint');
|
goog.require('ol.ViewHint');
|
||||||
|
goog.require('ol.animation');
|
||||||
goog.require('ol.interaction.ConditionType');
|
goog.require('ol.interaction.ConditionType');
|
||||||
goog.require('ol.interaction.Drag');
|
goog.require('ol.interaction.Drag');
|
||||||
|
|
||||||
@@ -16,8 +17,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 +29,12 @@ ol.interaction.DragPan = function(condition) {
|
|||||||
*/
|
*/
|
||||||
this.condition_ = condition;
|
this.condition_ = condition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.Kinetic|undefined}
|
||||||
|
*/
|
||||||
|
this.kinetic_ = opt_kinetic;
|
||||||
|
|
||||||
};
|
};
|
||||||
goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
|
goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
|
||||||
|
|
||||||
@@ -35,6 +43,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 +66,31 @@ 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();
|
||||||
|
var kineticPanFrom = ol.animation.createPanFrom({
|
||||||
|
source: center,
|
||||||
|
duration: this.kinetic_.getDuration(),
|
||||||
|
easing: this.kinetic_.getEasingFn()
|
||||||
|
});
|
||||||
|
map.addPreRenderFunction(kineticPanFrom);
|
||||||
|
|
||||||
|
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 +100,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);
|
||||||
|
|||||||
143
src/ol/kinetic.js
Normal file
143
src/ol/kinetic.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
|
||||||
|
goog.provide('ol.Kinetic');
|
||||||
|
|
||||||
|
goog.require('goog.array');
|
||||||
|
goog.require('ol.Pixel');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{x: number,
|
||||||
|
* y: number,
|
||||||
|
* t: number}}
|
||||||
|
*/
|
||||||
|
ol.KineticPoint;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {number} decay Rate of decay (must be negative).
|
||||||
|
* @param {number} v_min Minimum velocity (pixels/millisecond).
|
||||||
|
* @param {number} delay Delay to consider to calculate the kinetic
|
||||||
|
* initial values (milliseconds).
|
||||||
|
*/
|
||||||
|
ol.Kinetic = function(decay, v_min, delay) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.decay_ = decay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.v_min_ = v_min;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.delay_ = delay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Array.<ol.KineticPoint>}
|
||||||
|
*/
|
||||||
|
this.points_ = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.angle_ = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.v_0_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {goog.events.BrowserEvent} browserEvent Browser event.
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.begin = function(browserEvent) {
|
||||||
|
this.points_.length = 0;
|
||||||
|
this.angle_ = 0;
|
||||||
|
this.v_0_ = 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 index = Math.abs(goog.array.binarySelect(this.points_, function(elt) {
|
||||||
|
return elt.t < now - this.delay_;
|
||||||
|
}, this));
|
||||||
|
if (index < this.points_.length - 1) {
|
||||||
|
var first = this.points_[index];
|
||||||
|
var last = this.points_[this.points_.length - 1];
|
||||||
|
var dx = last.x - first.x;
|
||||||
|
var dy = last.y - first.y;
|
||||||
|
this.angle_ = Math.atan2(dy, dx);
|
||||||
|
this.v_0_ = Math.sqrt(dx * dx + dy * dy) / (last.t - first.t);
|
||||||
|
return this.v_0_ > this.v_min_;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {function(number): number} Easing function for kinetic animation.
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getEasingFn = function() {
|
||||||
|
var decay = this.decay_;
|
||||||
|
var v_0 = this.v_0_;
|
||||||
|
var v_min = this.v_min_;
|
||||||
|
var duration = this.getDuration();
|
||||||
|
return function(t) {
|
||||||
|
return v_0 * (Math.exp((decay * t) * duration) - 1) / (v_min - v_0);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} Duration of animation (milliseconds).
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getDuration = function() {
|
||||||
|
return Math.log(this.v_min_ / this.v_0_) / this.decay_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} Total distance travelled (pixels).
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getDistance = function() {
|
||||||
|
return (this.v_min_ - this.v_0_) / this.decay_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} Angle of the kinetic panning animation (radians).
|
||||||
|
*/
|
||||||
|
ol.Kinetic.prototype.getAngle = function() {
|
||||||
|
return this.angle_;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user