Comparing version 2.0.2 to 2.0.3
41
API.md
@@ -7,6 +7,16 @@ # Easing | ||
## var ease = mapbox.ease() | ||
## mapbox.ease() | ||
**Returns** an easey object, which has the following methods: | ||
### ease.map(map) | ||
Specify the map to be used. | ||
**Arguments:** | ||
* `map` is an instance of `mapbox.map()` or `MM.Map` | ||
**Returns** the easey object. | ||
### ease.from(coord) | ||
@@ -44,3 +54,24 @@ | ||
### ease.optimal([V] [,rho] [,callback]) | ||
Eases to `from` in the smoothest way possible, automatically choosing run time based on distance. The easing zooms out and in to optimize for the shortest easing time and the slowest percieved speed. The optional arguments are as defined in *[Smooth and efficient zooming and panning](http://www.cs.ubc.ca/~tmm/courses/cpsc533c-04-spr/readings/zoompan.pdf)*. | ||
**Arguments:** | ||
* `V` inversely affects the speed (default is 0.9) | ||
* `rho` affects the sensitivity of zooming in and out (default 1.42) | ||
* `callback` is a function that gets called after the animation completes. | ||
**Returns** the easey object. | ||
### ease.location(location) | ||
Sets the `to` coordinate to the provided location. | ||
**Arguments:** | ||
* `location` is an object with `lat` and `lon` properties, or an instance of `MM.Location`. | ||
**Returns** the easey object. | ||
### ease.t(value) | ||
@@ -104,10 +135,14 @@ | ||
### var isRunning = ease.running() | ||
### ease.running() | ||
**Returns** `true` or `false` depending on whether easey is currently animating the map. | ||
### ease.stop() | ||
### ease.stop([callback]) | ||
Abort the currently running animation. | ||
**Arguments:** | ||
* `callback` is a function to be called after the previous animation has been successfully stopped. | ||
**Returns** the easey object. |
{ | ||
"name": "easey", | ||
"version": "2.0.2", | ||
"version": "2.0.3", | ||
"description": "Easing for Modest Maps", | ||
@@ -5,0 +5,0 @@ "author": { |
@@ -8,51 +8,25 @@ ;(function(context, MM) { | ||
map, | ||
prevT = 0, | ||
acceleration = 25.0, | ||
speed = null, | ||
panner, | ||
maxTapTime = 250, | ||
maxTapDistance = 30, | ||
maxDoubleTapDelay = 350, | ||
drag = 0.10, | ||
locations = {}, | ||
taps = [], | ||
wasPinching = false, | ||
nowPoint = null, | ||
oldPoint = null, | ||
lastMove = null, | ||
lastPinchCenter = null; | ||
lastPinchCenter = null, | ||
p0 = new MM.Point(0, 0), | ||
p1 = new MM.Point(0, 0); | ||
function animate(t) { | ||
var dir = { x: 0, y: 0 }; | ||
var dt = Math.max(0.001, (t - prevT) / 1000.0); | ||
if (nowPoint && oldPoint && | ||
(lastMove > (+new Date() - 50))) { | ||
dir.x = nowPoint.x - oldPoint.x; | ||
dir.y = nowPoint.y - oldPoint.y; | ||
speed.x = dir.x; | ||
speed.y = dir.y; | ||
} else { | ||
speed.x -= speed.x * drag; | ||
speed.y -= speed.y * drag; | ||
if (Math.abs(speed.x) < 0.001) { | ||
speed.x = 0; | ||
function focusMap(e) { | ||
map.parent.focus(); | ||
} | ||
function clearLocations() { | ||
for (var loc in locations) { | ||
if (locations.hasOwnProperty(loc)) { | ||
delete locations[loc]; | ||
} | ||
if (Math.abs(speed.y) < 0.001) { | ||
speed.y = 0; | ||
} | ||
} | ||
if (speed.x || speed.y) { | ||
map.panBy(speed.x, speed.y); | ||
} | ||
prevT = t; | ||
// tick every frame for time-based anim accuracy | ||
MM.getFrame(animate); | ||
} | ||
// Test whether touches are from the same source - | ||
// whether this is the same touchmove event. | ||
function sameTouch (event, touch) { | ||
return (event && event.touch) && | ||
(touch.identifier == event.touch.identifier); | ||
} | ||
function updateTouches (e) { | ||
@@ -70,2 +44,3 @@ for (var i = 0; i < e.touches.length; i += 1) { | ||
startPos: { x: t.clientX, y: t.screenY }, | ||
startZoom: map.zoom(), | ||
x: t.clientX, | ||
@@ -80,3 +55,14 @@ y: t.clientY, | ||
function touchStartMachine(e) { | ||
MM.addEvent(e.touches[0].target, 'touchmove', | ||
touchMoveMachine); | ||
MM.addEvent(e.touches[0].target, 'touchend', | ||
touchEndMachine); | ||
if (e.touches[1]) { | ||
MM.addEvent(e.touches[1].target, 'touchmove', | ||
touchMoveMachine); | ||
MM.addEvent(e.touches[1].target, 'touchend', | ||
touchEndMachine); | ||
} | ||
updateTouches(e); | ||
panner.down(e.touches[0]); | ||
return MM.cancelEvent(e); | ||
@@ -88,7 +74,7 @@ } | ||
case 1: | ||
onPanning(e.touches[0]); | ||
break; | ||
panner.move(e.touches[0]); | ||
break; | ||
case 2: | ||
onPinching(e); | ||
break; | ||
break; | ||
} | ||
@@ -104,4 +90,4 @@ updateTouches(e); | ||
onDoubleTap(tap); | ||
taps = []; | ||
return; | ||
taps = []; | ||
return; | ||
} | ||
@@ -119,6 +105,7 @@ taps = [tap]; | ||
map.dispatchCallback('zoomed'); | ||
clearLocations(); | ||
}); | ||
} | ||
function isTouchable () { | ||
function isTouchable() { | ||
var el = document.createElement('div'); | ||
@@ -129,16 +116,10 @@ el.setAttribute('ongesturestart', 'return;'); | ||
// Re-transform the actual map parent's CSS transformation | ||
function onPanning(touch) { | ||
lastMove = +new Date(); | ||
oldPoint = nowPoint; | ||
nowPoint = { x: touch.clientX, y: touch.clientY }; | ||
} | ||
function onPinching(e) { | ||
// use the first two touches and their previous positions | ||
var t0 = e.touches[0], | ||
t1 = e.touches[1], | ||
p0 = new MM.Point(t0.clientX, t0.clientY), | ||
p1 = new MM.Point(t1.clientX, t1.clientY), | ||
t1 = e.touches[1]; | ||
p0.x = t0.clientX; | ||
p0.y = t0.clientY; | ||
p1.x = t1.clientX; | ||
p1.y = t1.clientY; | ||
l0 = locations[t0.identifier], | ||
@@ -155,23 +136,21 @@ l1 = locations[t1.identifier]; | ||
map.zoomByAbout( | ||
Math.log(e.scale) / Math.LN2 - | ||
Math.log(l0.scale) / Math.LN2, | ||
Math.log(e.scale) / Math.LN2 - Math.log(l0.scale) / Math.LN2, | ||
center); | ||
// pan from the previous center of these touches | ||
var prevCenter = MM.Point.interpolate(l0, l1, 0.5); | ||
map.panBy(center.x - prevCenter.x, | ||
center.y - prevCenter.y); | ||
wasPinching = true; | ||
lastPinchCenter = center; | ||
// pan from the previous center of these touches | ||
prevX = l0.x + (l1.x - l0.x) * 0.5; | ||
prevY = l0.y + (l1.y - l0.y) * 0.5; | ||
map.panBy(center.x - prevX, | ||
center.y - prevY); | ||
wasPinching = true; | ||
lastPinchCenter = center; | ||
} | ||
// When a pinch event ends, round the zoom of the map. | ||
function onPinched(p) { | ||
// TODO: easing | ||
if (true) { | ||
var z = map.getZoom(), // current zoom | ||
tz = Math.round(z); // target zoom | ||
map.zoomByAbout(tz - z, p); | ||
} | ||
function onPinched(touch) { | ||
var z = map.getZoom(), // current zoom | ||
tz = locations[touch.identifier].startZoom > z ? Math.floor(z) : Math.ceil(z); | ||
easey().map(map).point(lastPinchCenter).zoom(tz) | ||
.path('about').run(300); | ||
clearLocations(); | ||
wasPinching = false; | ||
@@ -181,9 +160,14 @@ } | ||
function touchEndMachine(e) { | ||
MM.removeEvent(e.target, 'touchmove', | ||
touchMoveMachine); | ||
MM.removeEvent(e.target, 'touchend', | ||
touchEndMachine); | ||
var now = new Date().getTime(); | ||
// round zoom if we're done pinching | ||
if (e.touches.length === 0 && wasPinching) { | ||
onPinched(lastPinchCenter); | ||
onPinched(e.changedTouches[0]); | ||
} | ||
oldPoint = nowPoint = null; | ||
panner.up(); | ||
@@ -247,10 +231,3 @@ // Look at each changed touch in turn. | ||
touchStartMachine); | ||
MM.addEvent(map.parent, 'touchmove', | ||
touchMoveMachine); | ||
MM.addEvent(map.parent, 'touchend', | ||
touchEndMachine); | ||
prevT = new Date().getTime(); | ||
speed = { x: 0, y: 0 }; | ||
MM.getFrame(animate); | ||
panner = panning(0.10); | ||
}; | ||
@@ -264,6 +241,3 @@ | ||
touchStartMachine); | ||
MM.removeEvent(map.parent, 'touchmove', | ||
touchMoveMachine); | ||
MM.removeEvent(map.parent, 'touchend', | ||
touchEndMachine); | ||
panner.remove(); | ||
}; | ||
@@ -376,13 +350,3 @@ | ||
map, | ||
prevT = 0, | ||
speed = null, | ||
drag = 0.15, | ||
removed = false, | ||
mouseDownPoint = null, | ||
mouseDownTime = 0, | ||
mousePoint = null, | ||
prevMousePoint = null, | ||
moveTime = null, | ||
prevMoveTime = null, | ||
animatedLastPoint = true; | ||
panner; | ||
@@ -397,4 +361,3 @@ function focusMap(e) { | ||
MM.addEvent(document, 'mouseup', mouseUp); | ||
mousePoint = prevMousePoint = MM.getMousePoint(e, map); | ||
moveTime = prevMoveTime = +new Date(); | ||
panner.down(e); | ||
map.parent.style.cursor = 'move'; | ||
@@ -405,22 +368,70 @@ return MM.cancelEvent(e); | ||
function mouseMove(e) { | ||
if (mousePoint) { | ||
panner.move(e); | ||
return MM.cancelEvent(e); | ||
} | ||
function mouseUp(e) { | ||
MM.removeEvent(document, 'mousemove', mouseMove); | ||
MM.removeEvent(document, 'mouseup', mouseUp); | ||
panner.up(); | ||
map.parent.style.cursor = ''; | ||
return MM.cancelEvent(e); | ||
} | ||
handler.init = function(x) { | ||
map = x; | ||
MM.addEvent(map.parent, 'click', focusMap); | ||
MM.addEvent(map.parent, 'mousedown', mouseDown); | ||
panner = panning(); | ||
}; | ||
handler.remove = function() { | ||
MM.removeEvent(map.parent, 'click', focusMap); | ||
MM.removeEvent(map.parent, 'mousedown', mouseDown); | ||
panner.up(); | ||
panner.remove(); | ||
}; | ||
return handler; | ||
}; | ||
function panning(drag) { | ||
var p = {}; | ||
drag = drag || 0.15; | ||
var speed = { x: 0, y: 0 }, | ||
dir = { x: 0, y: 0 }, | ||
removed = false, | ||
nowPoint = null, | ||
oldPoint = null, | ||
moveTime = null, | ||
prevMoveTime = null, | ||
animatedLastPoint = true, | ||
t, | ||
prevT = new Date().getTime(); | ||
p.down = function(e) { | ||
nowPoint = oldPoint = MM.getMousePoint(e, map); | ||
moveTime = prevMoveTime = +new Date(); | ||
}; | ||
p.move = function(e) { | ||
if (nowPoint) { | ||
if (animatedLastPoint) { | ||
prevMousePoint = mousePoint; | ||
oldPoint = nowPoint; | ||
prevMoveTime = moveTime; | ||
animatedLastPoint = false; | ||
} | ||
mousePoint = MM.getMousePoint(e, map); | ||
nowPoint = MM.getMousePoint(e, map); | ||
moveTime = +new Date(); | ||
return MM.cancelEvent(e); | ||
} | ||
} | ||
}; | ||
function mouseUp(e) { | ||
MM.removeEvent(document, 'mousemove', mouseMove); | ||
MM.removeEvent(document, 'mouseup', mouseUp); | ||
p.up = function() { | ||
if (+new Date() - prevMoveTime < 50) { | ||
dt = Math.max(1, moveTime - prevMoveTime); | ||
var dir = { x: 0, y: 0 }; | ||
dir.x = mousePoint.x - prevMousePoint.x; | ||
dir.y = mousePoint.y - prevMousePoint.y; | ||
dir.x = nowPoint.x - oldPoint.x; | ||
dir.y = nowPoint.y - oldPoint.y; | ||
speed.x = dir.x / dt; | ||
@@ -432,15 +443,16 @@ speed.y = dir.y / dt; | ||
} | ||
mousePoint = prevMousePoint = null; | ||
moveTime = lastMoveTime = null; | ||
map.parent.style.cursor = ''; | ||
return MM.cancelEvent(e); | ||
} | ||
nowPoint = oldPoint = null; | ||
moveTime = null; | ||
}; | ||
p.remove = function() { | ||
removed = true; | ||
}; | ||
function animate(t) { | ||
var dir = { x: 0, y: 0 }; | ||
var dt = Math.max(1, t - prevT); | ||
if (mousePoint && prevMousePoint) { | ||
if (nowPoint && oldPoint) { | ||
if (!animatedLastPoint) { | ||
dir.x = mousePoint.x - prevMousePoint.x; | ||
dir.y = mousePoint.y - prevMousePoint.y; | ||
dir.x = nowPoint.x - oldPoint.x; | ||
dir.y = nowPoint.y - oldPoint.y; | ||
map.panBy(dir.x, dir.y); | ||
@@ -468,23 +480,9 @@ animatedLastPoint = true; | ||
handler.init = function(x) { | ||
map = x; | ||
MM.addEvent(map.parent, 'click', focusMap); | ||
MM.addEvent(map.parent, 'mousedown', mouseDown); | ||
prevT = new Date().getTime(); | ||
speed = { x: 0, y: 0 }; | ||
removed = false; | ||
MM.getFrame(animate); | ||
}; | ||
MM.getFrame(animate); | ||
return p; | ||
} | ||
handler.remove = function() { | ||
MM.removeEvent(map.parent, 'click', focusMap); | ||
MM.removeEvent(map.parent, 'mousedown', mouseDown); | ||
removed = true; | ||
}; | ||
return handler; | ||
}; | ||
this.easey_handlers = easey_handlers; | ||
})(this, MM); |
105
src/easey.js
@@ -84,24 +84,31 @@ ;(function(context, MM) { | ||
var paths = {}; | ||
var paths = {}, | ||
static_coord = new MM.Coordinate(0, 0, 0); | ||
// The screen path simply moves between | ||
// coordinates in a non-geographical way | ||
paths.screen = function(a, b, t) { | ||
paths.screen = function(a, b, t, static_coord) { | ||
var zoom_lerp = interp(a.zoom, b.zoom, t); | ||
var az = a.copy(); | ||
var bz = b.copy().zoomTo(a.zoom); | ||
return (new MM.Coordinate( | ||
interp(az.row, bz.row, t), | ||
interp(az.column, bz.column, t), | ||
az.zoom)).zoomTo(zoom_lerp); | ||
if (static_coord) { | ||
static_coord.row = interp( | ||
a.row, | ||
b.row * Math.pow(2, a.zoom - b.zoom), | ||
t) * Math.pow(2, zoom_lerp - a.zoom); | ||
static_coord.column = interp( | ||
a.column, | ||
b.column * Math.pow(2, a.zoom - b.zoom), | ||
t) * Math.pow(2, zoom_lerp - a.zoom); | ||
static_coord.zoom = zoom_lerp; | ||
} else { | ||
return new MM.Coordinate( | ||
interp(a.row, | ||
b.row * Math.pow(2, a.zoom - b.zoom), | ||
t) * Math.pow(2, zoom_lerp - a.zoom), | ||
interp(a.column, | ||
b.column * Math.pow(2, a.zoom - b.zoom), | ||
t) * Math.pow(2, zoom_lerp - a.zoom), | ||
zoom_lerp); | ||
} | ||
}; | ||
function ptWithCoords(a, b) { | ||
// distance from the center of the map | ||
var point = new MM.Point(map.dimensions.x / 2, map.dimensions.y / 2); | ||
point.x += map.tileSize.x * (b.column - a.column); | ||
point.y += map.tileSize.y * (b.row - a.row); | ||
return point; | ||
} | ||
// The screen path means that the b | ||
@@ -111,15 +118,30 @@ // coordinate should maintain its point on screen | ||
// should move to its zoom level | ||
paths.about = function(a, b, t) { | ||
paths.about = function(a, b, t, static_coord) { | ||
var zoom_lerp = interp(a.zoom, b.zoom, t); | ||
var bs = b.copy().zoomTo(a.zoom); | ||
var az = a.copy().zoomTo(zoom_lerp); | ||
var bz = b.copy().zoomTo(zoom_lerp); | ||
var start = ptWithCoords(a, bs); | ||
var end = ptWithCoords(az, bz); | ||
// center x, center y | ||
var cx = map.dimensions.x / 2, | ||
cy = map.dimensions.y / 2, | ||
// tilesize | ||
tx = map.tileSize.x, | ||
ty = map.tileSize.y; | ||
az.column -= (start.x - end.x) / map.tileSize.x; | ||
az.row -= (start.y - end.y) / map.tileSize.y; | ||
var startx = cx + tx * ((b.column * Math.pow(2, a.zoom - b.zoom)) - a.column); | ||
var starty = cy + ty * ((b.row * Math.pow(2, a.zoom - b.zoom)) - a.row); | ||
return az; | ||
var endx = cx + tx * ((b.column * Math.pow(2, zoom_lerp - b.zoom)) - | ||
(a.column * Math.pow(2, zoom_lerp - a.zoom))); | ||
var endy = cy + ty * ((b.row * Math.pow(2, zoom_lerp - b.zoom)) - (a.row * | ||
Math.pow(2, zoom_lerp - a.zoom))); | ||
if (static_coord) { | ||
static_coord.column = (a.column * Math.pow(2, zoom_lerp - a.zoom)) - ((startx - endx) / tx); | ||
static_coord.row = (a.row * Math.pow(2, zoom_lerp - a.zoom)) - ((starty - endy) / ty); | ||
static_coord.zoom = zoom_lerp; | ||
} else { | ||
return new MM.Coordinate( | ||
(a.column * Math.pow(2, zoom_lerp - a.zoom)) - ((startx - endx) / tx), | ||
(a.row * Math.pow(2, zoom_lerp - a.zoom)) - ((starty - endy) / ty), | ||
zoom_lerp); | ||
} | ||
}; | ||
@@ -130,3 +152,4 @@ | ||
easey.t = function(t) { | ||
map.coordinate = path(from, to, easing(t)); | ||
path(from, to, easing(t), static_coord); | ||
map.coordinate = static_coord; | ||
map.draw(); | ||
@@ -169,4 +192,6 @@ return easey; | ||
} else if (delta > time) { | ||
if (to.zoom != from.zoom) map.dispatchCallback('zoomed', to.zoom - from.zoom); | ||
running = false; | ||
map.coordinate = path(from, to, 1); | ||
path(from, to, 1, static_coord); | ||
map.coordinate = static_coord; | ||
to = from = undefined; | ||
@@ -176,3 +201,4 @@ map.draw(); | ||
} else { | ||
map.coordinate = path(from, to, easing(delta / time)); | ||
path(from, to, easing(delta / time), static_coord); | ||
map.coordinate = static_coord; | ||
map.draw(); | ||
@@ -259,4 +285,11 @@ MM.getFrame(tick); | ||
var oldpath = path; | ||
path = function (a, b, t) { | ||
if (t == 1) return to; | ||
path = function (a, b, t, static_coord) { | ||
if (t == 1) { | ||
if (static_coord) { | ||
static_coord.row = to.row; | ||
static_coord.column = to.column; | ||
static_coord.zoom = to.zoom; | ||
} | ||
return to; | ||
} | ||
var s = t * S, | ||
@@ -267,3 +300,11 @@ us = u(s), | ||
y = interp(c0.y, c1.y, us/u1 || 1); | ||
return new MM.Coordinate(y, x, 0).zoomTo(z); | ||
var power = Math.pow(2, z); | ||
if (static_coord) { | ||
static_coord.row = y * power; | ||
static_coord.column = x * power; | ||
static_coord.zoom = z; | ||
} else { | ||
return new MM.Coordinate(y * power, x * power, z); | ||
} | ||
}; | ||
@@ -281,2 +322,4 @@ | ||
this.easey = easey; | ||
if (typeof this.mapbox == 'undefined') this.mapbox = {}; | ||
this.mapbox.ease = easey; | ||
})(this, MM); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
465645
10081