diff --git a/debug/map/max-bounds-bouncy.html b/debug/map/max-bounds-bouncy.html
new file mode 100644
index 000000000..4fbb4bc87
--- /dev/null
+++ b/debug/map/max-bounds-bouncy.html
@@ -0,0 +1,52 @@
+
+
+
+ Leaflet debug page
+
+
+
+
+
+
+
+
+
+
+
+
+ Left: Bouncy maxBounds. Right: Not bouncy.
+
+
+
+
+
+
+
diff --git a/src/map/Map.js b/src/map/Map.js
index ef93d6083..f0c0c1625 100644
--- a/src/map/Map.js
+++ b/src/map/Map.js
@@ -15,7 +15,8 @@ L.Map = L.Evented.extend({
fadeAnimation: true,
trackResize: true,
- markerZoomAnimation: true
+ markerZoomAnimation: true,
+ maxBoundsViscosity: 0.0
},
initialize: function (id, options) { // (HTMLElement or String, Object)
diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js
index f61f231d7..1d8251fd2 100644
--- a/src/map/handler/Map.Drag.js
+++ b/src/map/handler/Map.Drag.js
@@ -28,8 +28,9 @@ L.Map.Drag = L.Handler.extend({
dragend: this._onDragEnd
}, this);
+ this._draggable.on('predrag', this._onPreDragLimit, this);
if (map.options.worldCopyJump) {
- this._draggable.on('predrag', this._onPreDrag, this);
+ this._draggable.on('predrag', this._onPreDragWrap, this);
map.on('viewreset', this._onViewReset, this);
map.whenReady(this._onViewReset, this);
@@ -55,6 +56,19 @@ L.Map.Drag = L.Handler.extend({
_onDragStart: function () {
var map = this._map;
+ if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
+ var bounds = L.latLngBounds(this._map.options.maxBounds);
+
+ this._offsetLimit = L.bounds(
+ this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
+ this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
+ .add(this._map.getSize()));
+
+ this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
+ } else {
+ this._offsetLimit = null;
+ }
+
map
.fire('movestart')
.fire('dragstart');
@@ -92,7 +106,25 @@ L.Map.Drag = L.Handler.extend({
this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
},
- _onPreDrag: function () {
+ _viscousLimit: function(value, threshold) {
+ return value - (value - threshold) * this._viscosity;
+ },
+
+ _onPreDragLimit: function () {
+ if (!this._viscosity || !this._offsetLimit) { return; }
+
+ var offset = this._draggable._newPos.subtract(this._draggable._startPos);
+
+ var limit = this._offsetLimit;
+ if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
+ if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
+ if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
+ if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
+
+ this._draggable._newPos = this._draggable._startPos.add(offset);
+ },
+
+ _onPreDragWrap: function () {
// TODO refactor to be able to adjust map pane position after zoom
var worldWidth = this._worldWidth,
halfWidth = Math.round(worldWidth / 2),