diff --git a/js/ui/camera.js b/js/ui/camera.js index d9a02de6530..06ac91feab5 100644 --- a/js/ui/camera.js +++ b/js/ui/camera.js @@ -6,6 +6,7 @@ var browser = require('../util/browser'); var LngLat = require('../geo/lng_lat'); var LngLatBounds = require('../geo/lng_lat_bounds'); var Point = require('point-geometry'); +var Transform = require('../geo/transform'); /** * Options common to {@link Map#jumpTo}, {@link Map#easeTo}, and {@link Map#flyTo}, @@ -408,13 +409,27 @@ util.extend(Camera.prototype, /** @lends Map.prototype */{ startZoom = this.getZoom(), startBearing = this.getBearing(), startPitch = this.getPitch(), + startCenter = tr.center, zoom = 'zoom' in options ? +options.zoom : startZoom, bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing, pitch = 'pitch' in options ? +options.pitch : startPitch, toLngLat, - toPoint; + toPoint, + + offsetLocation, + toLocation, + fromLocation, + endTransform; + + // Setup a Transform representing the state of the map at the end of the transition + endTransform = new Transform(); + endTransform.center = tr.center; + endTransform.resize(tr.width, tr.height); + endTransform.zoom = zoom; + endTransform.bearing = bearing; + endTransform.pitch = 0; // fixes #3119 by pretending the map is not pitched; use endTransform.pitch = pitch to revert to the old behavior if ('center' in options) { toLngLat = LngLat.convert(options.center); @@ -427,8 +442,6 @@ util.extend(Camera.prototype, /** @lends Map.prototype */{ toLngLat = tr.pointLocation(toPoint); } - var fromPoint = tr.locationPoint(toLngLat); - if (options.animate === false) options.duration = 0; this.zooming = (zoom !== startZoom); @@ -442,11 +455,31 @@ util.extend(Camera.prototype, /** @lends Map.prototype */{ this.fire('zoomstart', eventData); } + offsetLocation = endTransform.pointLocation(toPoint); + fromLocation = endTransform.pointLocation(endTransform.centerPoint); + + toLocation = LngLat.convert([ + toLngLat.lng - (offsetLocation.lng - fromLocation.lng), + toLngLat.lat - (offsetLocation.lat - fromLocation.lat) + ]); + clearTimeout(this._onEaseEnd); this._ease(function (k) { + // When zooming we need to scale our lat/lon interpolation because the distances change over the course of the transition + var k2 = k, + deltaZoom = zoom - startZoom, + totalDistanceExpansion = Math.pow(0.5, deltaZoom) - 1, + currentDelta, + currentDistanceExpansion; + if (this.zooming) { tr.zoom = interpolate(startZoom, zoom, k); + + currentDelta = tr.zoom - startZoom; + currentDistanceExpansion = Math.pow(0.5, currentDelta) - 1; + + k2 = currentDistanceExpansion / totalDistanceExpansion; } if (this.rotating) { @@ -457,7 +490,9 @@ util.extend(Camera.prototype, /** @lends Map.prototype */{ tr.pitch = interpolate(startPitch, pitch, k); } - tr.setLocationAtPoint(toLngLat, fromPoint.add(toPoint.sub(fromPoint)._mult(k))); + var lng = interpolate(startCenter.lng, toLocation.lng, k2); + var lat = interpolate(startCenter.lat, toLocation.lat, k2); + tr.center = LngLat.convert([lng, lat]); this.fire('move', eventData); if (this.zooming) {