Fractional zoom controls.
This commit is contained in:
parent
b85e902970
commit
65ccc2b36c
107
debug/map/zoom-delta.html
Normal file
107
debug/map/zoom-delta.html
Normal file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Leaflet debug page</title>
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0" />
|
||||
|
||||
<link rel="stylesheet" href="../../dist/leaflet.css" />
|
||||
|
||||
<link rel="stylesheet" href="../css/mobile.css" />
|
||||
|
||||
<script type="text/javascript" src="../../build/deps.js"></script>
|
||||
<script src="../leaflet-include.js"></script>
|
||||
<style>
|
||||
.container {
|
||||
float:left; width: 600px; height: 600px;
|
||||
position: relative;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
#map1, #map2 {
|
||||
position:absolute;
|
||||
top:2em;
|
||||
bottom:2em;
|
||||
left:0;
|
||||
right:0;
|
||||
}
|
||||
#zoom1, #zoom2 {
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
left:0;
|
||||
right:0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Zoom delta test.</h1>
|
||||
|
||||
<p>Zooming with touch zoom, box zoom or flyTo then <code>map.stop()</code> must make the zoom level snap to the value of the <code>zoomSnap</code> option. Zoom interactions (keyboard, mouse wheel, zoom control buttons must change the zoom by the amount in the <code>zoomDelta</code> option.</p>
|
||||
|
||||
<div>
|
||||
<button id="sf">SF</button>
|
||||
<button id="trd">TRD</button>
|
||||
<button id="stop">stop</button>
|
||||
</div>
|
||||
|
||||
<div class='container'>
|
||||
Snap: 0.25. Delta: 0.5.
|
||||
<div id="map1"></div>
|
||||
<span id="zoom1"></span>
|
||||
</div>
|
||||
<div class='container'>
|
||||
Snap: 0 (off). Delta: 0.25.
|
||||
<div id="map2"></div>
|
||||
<span id="zoom2"></span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var sf = [37.77, -122.42],
|
||||
trd = [63.41, 10.41];
|
||||
|
||||
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
osmAttrib = '© <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
osm1 = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
|
||||
osm2 = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
|
||||
center = L.latLng(63.41, 10.41);
|
||||
|
||||
var map1 = new L.Map('map1', {
|
||||
center: center,
|
||||
layers: [osm1],
|
||||
zoom: 5,
|
||||
zoomSnap: 0.25,
|
||||
zoomDelta: 0.5
|
||||
});
|
||||
|
||||
var map2 = new L.Map('map2', {
|
||||
center: center,
|
||||
layers: [osm2],
|
||||
zoom: 5,
|
||||
zoomSnap: 0,
|
||||
zoomDelta: 0.25
|
||||
});
|
||||
|
||||
map1.on('zoomend',function(){
|
||||
document.getElementById('zoom1').innerHTML = "Zoom level: " + map1.getZoom();
|
||||
});
|
||||
map2.on('zoomend',function(){
|
||||
document.getElementById('zoom2').innerHTML = "Zoom level: " + map2.getZoom();
|
||||
});
|
||||
|
||||
document.getElementById('sf').onclick = function () {
|
||||
map1.flyTo(sf, 10, {duration: 20});
|
||||
map2.flyTo(sf, 10, {duration: 20});
|
||||
};
|
||||
document.getElementById('trd').onclick = function () {
|
||||
map1.flyTo(trd, 10, {duration: 20});
|
||||
map2.flyTo(trd, 10, {duration: 20});
|
||||
};
|
||||
document.getElementById('stop').onclick = function () {
|
||||
map1.stop();
|
||||
map2.stop();
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -624,6 +624,142 @@ describe("Map", function () {
|
||||
map.setView([0, 0], 0);
|
||||
map.once('zoomend', callback).flyTo(newCenter, newZoom);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#zoomIn and #zoomOut', function () {
|
||||
var center = L.latLng(22, 33);
|
||||
beforeEach(function () {
|
||||
map.setView(center, 10);
|
||||
});
|
||||
|
||||
it('zoomIn zooms by 1 zoom level by default', function (done) {
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(11);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
map.zoomIn(null, {animate: false});
|
||||
});
|
||||
|
||||
it('zoomOut zooms by 1 zoom level by default', function (done) {
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(9);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
map.zoomOut(null, {animate: false});
|
||||
});
|
||||
|
||||
it('zoomIn ignores the zoomDelta option on non-any3d browsers', function (done) {
|
||||
L.Browser.any3d = false;
|
||||
map.options.zoomSnap = 0.25;
|
||||
map.options.zoomDelta = 0.25;
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(11);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
map.zoomIn(null, {animate: false});
|
||||
});
|
||||
|
||||
it('zoomIn respects the zoomDelta option on any3d browsers', function (done) {
|
||||
L.Browser.any3d = true;
|
||||
map.options.zoomSnap = 0.25;
|
||||
map.options.zoomDelta = 0.25;
|
||||
map.setView(center, 10);
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(10.25);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
map.zoomIn(null, {animate: false});
|
||||
});
|
||||
|
||||
it('zoomOut respects the zoomDelta option on any3d browsers', function (done) {
|
||||
L.Browser.any3d = true;
|
||||
map.options.zoomSnap = 0.25;
|
||||
map.options.zoomDelta = 0.25;
|
||||
map.setView(center, 10);
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(9.75);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
map.zoomOut(null, {animate: false});
|
||||
});
|
||||
|
||||
it('zoomIn snaps to zoomSnap on any3d browsers', function (done) {
|
||||
map.options.zoomSnap = 0.25;
|
||||
map.setView(center, 10);
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(10.25);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
L.Browser.any3d = true;
|
||||
map.zoomIn(0.22, {animate: false});
|
||||
});
|
||||
|
||||
it('zoomOut snaps to zoomSnap on any3d browsers', function (done) {
|
||||
map.options.zoomSnap = 0.25;
|
||||
map.setView(center, 10);
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(9.75);
|
||||
expect(map.getCenter()).to.eql(center);
|
||||
done();
|
||||
});
|
||||
L.Browser.any3d = true;
|
||||
map.zoomOut(0.22, {animate: false});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#fitBounds', function () {
|
||||
var center = L.latLng(22, 33),
|
||||
bounds = L.latLngBounds(L.latLng(1, 102), L.latLng(11, 122)),
|
||||
boundsCenter = bounds.getCenter();
|
||||
|
||||
beforeEach(function () {
|
||||
// fitBounds needs a map container with non-null area
|
||||
var container = map.getContainer();
|
||||
container.style.width = container.style.height = "100px";
|
||||
document.body.appendChild(container);
|
||||
map.setView(center, 10);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
document.body.removeChild(map.getContainer());
|
||||
});
|
||||
|
||||
it('Snaps zoom level to integer by default', function (done) {
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(2);
|
||||
expect(map.getCenter().equals(boundsCenter, 0.05)).to.eql(true);
|
||||
done();
|
||||
});
|
||||
map.fitBounds(bounds, {animate: false});
|
||||
});
|
||||
|
||||
it('Snaps zoom to zoomSnap on any3d browsers', function (done) {
|
||||
map.options.zoomSnap = 0.25;
|
||||
L.Browser.any3d = true;
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(2.75);
|
||||
expect(map.getCenter().equals(boundsCenter, 0.05)).to.eql(true);
|
||||
done();
|
||||
});
|
||||
map.fitBounds(bounds, {animate: false});
|
||||
});
|
||||
|
||||
it('Ignores zoomSnap on non-any3d browsers', function (done) {
|
||||
map.options.zoomSnap = 0.25;
|
||||
L.Browser.any3d = false;
|
||||
map.once('zoomend', function() {
|
||||
expect(map.getZoom()).to.eql(2);
|
||||
expect(map.getCenter().equals(boundsCenter, 0.05)).to.eql(true);
|
||||
done();
|
||||
});
|
||||
map.fitBounds(bounds, {animate: false});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -17,7 +17,9 @@ L.Map = L.Evented.extend({
|
||||
trackResize: true,
|
||||
markerZoomAnimation: true,
|
||||
maxBoundsViscosity: 0.0,
|
||||
transform3DLimit: 8388608 // Precision limit of a 32-bit float
|
||||
transform3DLimit: 8388608, // Precision limit of a 32-bit float
|
||||
zoomSnap: 1,
|
||||
zoomDelta: 1
|
||||
},
|
||||
|
||||
initialize: function (id, options) { // (HTMLElement or String, Object)
|
||||
@ -72,11 +74,13 @@ L.Map = L.Evented.extend({
|
||||
},
|
||||
|
||||
zoomIn: function (delta, options) {
|
||||
return this.setZoom(this._zoom + (delta || 1), options);
|
||||
delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
|
||||
return this.setZoom(this._zoom + delta, options);
|
||||
},
|
||||
|
||||
zoomOut: function (delta, options) {
|
||||
return this.setZoom(this._zoom - (delta || 1), options);
|
||||
delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
|
||||
return this.setZoom(this._zoom - delta, options);
|
||||
},
|
||||
|
||||
setZoomAround: function (latlng, zoom, options) {
|
||||
@ -232,11 +236,8 @@ L.Map = L.Evented.extend({
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
L.Util.cancelAnimFrame(this._flyToFrame);
|
||||
if (this._panAnim) {
|
||||
this._panAnim.stop();
|
||||
}
|
||||
return this;
|
||||
this.setZoom(this._limitZoom(this._zoom));
|
||||
return this._stop();
|
||||
},
|
||||
|
||||
// TODO handler.addTo
|
||||
@ -331,9 +332,10 @@ L.Map = L.Evented.extend({
|
||||
getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
|
||||
bounds = L.latLngBounds(bounds);
|
||||
|
||||
var zoom = this.getMinZoom() - (inside ? 1 : 0),
|
||||
var zoom,
|
||||
maxZoom = this.getMaxZoom(),
|
||||
size = this.getSize(),
|
||||
snap = L.Browser.any3d ? this.options.zoomSnap : 1,
|
||||
|
||||
nw = bounds.getNorthWest(),
|
||||
se = bounds.getSouthEast(),
|
||||
@ -343,8 +345,17 @@ L.Map = L.Evented.extend({
|
||||
|
||||
padding = L.point(padding || [0, 0]);
|
||||
|
||||
if (snap <= 0) {
|
||||
zoom = this.getZoom();
|
||||
boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding);
|
||||
var scale = Math.min(size.x / boundsSize.x , size.y / boundsSize.y);
|
||||
return this.getScaleZoom(scale, zoom);
|
||||
}
|
||||
|
||||
zoom = this.getMinZoom() - (inside ? snap : 0);
|
||||
|
||||
do {
|
||||
zoom++;
|
||||
zoom += snap;
|
||||
boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding).floor();
|
||||
zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y;
|
||||
|
||||
@ -354,7 +365,7 @@ L.Map = L.Evented.extend({
|
||||
return null;
|
||||
}
|
||||
|
||||
return inside ? zoom : zoom - 1;
|
||||
return inside ? zoom : zoom - snap;
|
||||
},
|
||||
|
||||
getSize: function () {
|
||||
@ -582,6 +593,14 @@ L.Map = L.Evented.extend({
|
||||
return this.fire('moveend');
|
||||
},
|
||||
|
||||
_stop: function() {
|
||||
L.Util.cancelAnimFrame(this._flyToFrame);
|
||||
if (this._panAnim) {
|
||||
this._panAnim.stop();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_rawPanBy: function (offset) {
|
||||
L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
|
||||
},
|
||||
@ -837,9 +856,11 @@ L.Map = L.Evented.extend({
|
||||
|
||||
_limitZoom: function (zoom) {
|
||||
var min = this.getMinZoom(),
|
||||
max = this.getMaxZoom();
|
||||
if (!L.Browser.any3d) { zoom = Math.round(zoom); }
|
||||
|
||||
max = this.getMaxZoom(),
|
||||
snap = L.Browser.any3d ? this.options.zoomSnap : 1;
|
||||
if (snap) {
|
||||
zoom = Math.round(zoom / snap) * snap;
|
||||
}
|
||||
return Math.max(min, Math.min(max, zoom));
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ L.Map.include({
|
||||
return this.setView(targetCenter, targetZoom, options);
|
||||
}
|
||||
|
||||
this.stop();
|
||||
this._stop();
|
||||
|
||||
var from = this.project(this.getCenter()),
|
||||
to = this.project(targetCenter),
|
||||
|
@ -10,7 +10,7 @@ L.Map.include({
|
||||
center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
|
||||
options = options || {};
|
||||
|
||||
this.stop();
|
||||
this._stop();
|
||||
|
||||
if (this._loaded && !options.reset && options !== true) {
|
||||
|
||||
|
@ -18,7 +18,8 @@ L.Map.DoubleClickZoom = L.Handler.extend({
|
||||
_onDoubleClick: function (e) {
|
||||
var map = this._map,
|
||||
oldZoom = map.getZoom(),
|
||||
zoom = e.originalEvent.shiftKey ? Math.ceil(oldZoom) - 1 : Math.floor(oldZoom) + 1;
|
||||
delta = map.options.zoomDelta,
|
||||
zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
|
||||
|
||||
if (map.options.doubleClickZoom === 'center') {
|
||||
map.setZoom(zoom);
|
||||
|
@ -54,7 +54,7 @@ L.Map.Drag = L.Handler.extend({
|
||||
},
|
||||
|
||||
_onDown: function () {
|
||||
this._map.stop();
|
||||
this._map._stop();
|
||||
},
|
||||
|
||||
_onDragStart: function () {
|
||||
|
@ -4,8 +4,7 @@
|
||||
|
||||
L.Map.mergeOptions({
|
||||
keyboard: true,
|
||||
keyboardPanOffset: 80,
|
||||
keyboardZoomOffset: 1
|
||||
keyboardPanDelta: 80
|
||||
});
|
||||
|
||||
L.Map.Keyboard = L.Handler.extend({
|
||||
@ -22,8 +21,8 @@ L.Map.Keyboard = L.Handler.extend({
|
||||
initialize: function (map) {
|
||||
this._map = map;
|
||||
|
||||
this._setPanOffset(map.options.keyboardPanOffset);
|
||||
this._setZoomOffset(map.options.keyboardZoomOffset);
|
||||
this._setPanDelta(map.options.keyboardPanDelta);
|
||||
this._setZoomDelta(map.options.zoomDelta);
|
||||
},
|
||||
|
||||
addHooks: function () {
|
||||
@ -84,7 +83,7 @@ L.Map.Keyboard = L.Handler.extend({
|
||||
this._map.fire('blur');
|
||||
},
|
||||
|
||||
_setPanOffset: function (pan) {
|
||||
_setPanDelta: function (pan) {
|
||||
var keys = this._panKeys = {},
|
||||
codes = this.keyCodes,
|
||||
i, len;
|
||||
@ -103,7 +102,7 @@ L.Map.Keyboard = L.Handler.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_setZoomOffset: function (zoom) {
|
||||
_setZoomDelta: function (zoom) {
|
||||
var keys = this._zoomKeys = {},
|
||||
codes = this.keyCodes,
|
||||
i, len;
|
||||
|
@ -40,10 +40,10 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
|
||||
|
||||
_performZoom: function () {
|
||||
var map = this._map,
|
||||
delta = this._delta,
|
||||
delta = this._delta * this._map.options.zoomDelta,
|
||||
zoom = map.getZoom();
|
||||
|
||||
map.stop(); // stop panning and fly animations if any
|
||||
map._stop(); // stop panning and fly animations if any
|
||||
|
||||
// map the delta with a sigmoid function to -4..4 range leaning on -1..1
|
||||
var d2 = Math.ceil(4 * Math.log(2 / (1 + Math.exp(-Math.abs(delta / 200)))) / Math.LN2);
|
||||
|
@ -36,7 +36,7 @@ L.Map.TouchZoom = L.Handler.extend({
|
||||
this._moved = false;
|
||||
this._zooming = true;
|
||||
|
||||
map.stop();
|
||||
map._stop();
|
||||
|
||||
L.DomEvent
|
||||
.on(document, 'touchmove', this._onTouchMove, this)
|
||||
@ -97,11 +97,8 @@ L.Map.TouchZoom = L.Handler.extend({
|
||||
.off(document, 'touchmove', this._onTouchMove)
|
||||
.off(document, 'touchend', this._onTouchEnd);
|
||||
|
||||
var zoom = this._zoom;
|
||||
zoom = this._map._limitZoom(zoom - this._startZoom > 0 ? Math.ceil(zoom) : Math.floor(zoom));
|
||||
|
||||
|
||||
this._map._animateZoom(this._center, zoom, true, true);
|
||||
// Pinch shall update GridLayers' levels only when snapzoom is off.
|
||||
this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.snapZoom);
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user