in modules/renderer/map.js [464:641]
function zoomPan(event, key, transform) {
var source = event && event.sourceEvent || event;
var eventTransform = transform || (event && event.transform);
var x = eventTransform.x;
var y = eventTransform.y;
var k = eventTransform.k;
// Special handling of 'wheel' events:
// They might be triggered by the user scrolling the mouse wheel,
// or 2-finger pinch/zoom gestures, the transform may need adjustment.
if (source && source.type === 'wheel') {
// assume that the gesture is already handled by pointer events
if (_pointerDown) return;
var detected = utilDetect();
var dX = source.deltaX;
var dY = source.deltaY;
var x2 = x;
var y2 = y;
var k2 = k;
var t0, p0, p1;
// Normalize mousewheel scroll speed (Firefox) - #3029
// If wheel delta is provided in LINE units, recalculate it in PIXEL units
// We are essentially redoing the calculations that occur here:
// https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
// See this for more info:
// https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
if (source.deltaMode === 1 /* LINE */) {
// Convert from lines to pixels, more if the user is scrolling fast.
// (I made up the exp function to roughly match Firefox to what Chrome does)
// These numbers should be floats, because integers are treated as pan gesture below.
var lines = Math.abs(source.deltaY);
var sign = (source.deltaY > 0) ? 1 : -1;
dY = sign * clamp(
Math.exp((lines - 1) * 0.75) * 4.000244140625,
4.000244140625, // min
350.000244140625 // max
);
// On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
// There doesn't seem to be any scroll acceleration.
// This multiplier increases the speed a little bit - #5512
if (detected.os !== 'mac') {
dY *= 5;
}
// recalculate x2,y2,k2
t0 = _isTransformed ? _transformLast : _transformStart;
p0 = _getMouseCoords(source);
p1 = t0.invert(p0);
k2 = t0.k * Math.pow(2, -dY / 500);
k2 = clamp(k2, kMin, kMax);
x2 = p0[0] - p1[0] * k2;
y2 = p0[1] - p1[1] * k2;
// 2 finger map pinch zooming (Safari) - #5492
// These are fake `wheel` events we made from Safari `gesturechange` events..
} else if (source._scale) {
// recalculate x2,y2,k2
t0 = _gestureTransformStart;
p0 = _getMouseCoords(source);
p1 = t0.invert(p0);
k2 = t0.k * source._scale;
k2 = clamp(k2, kMin, kMax);
x2 = p0[0] - p1[0] * k2;
y2 = p0[1] - p1[1] * k2;
// 2 finger map pinch zooming (all browsers except Safari) - #5492
// Pinch zooming via the `wheel` event will always have:
// - `ctrlKey = true`
// - `deltaY` is not round integer pixels (ignore `deltaX`)
} else if (source.ctrlKey && !isInteger(dY)) {
dY *= 6; // slightly scale up whatever the browser gave us
// recalculate x2,y2,k2
t0 = _isTransformed ? _transformLast : _transformStart;
p0 = _getMouseCoords(source);
p1 = t0.invert(p0);
k2 = t0.k * Math.pow(2, -dY / 500);
k2 = clamp(k2, kMin, kMax);
x2 = p0[0] - p1[0] * k2;
y2 = p0[1] - p1[1] * k2;
// Trackpad scroll zooming with shift or alt/option key down
} else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
// recalculate x2,y2,k2
t0 = _isTransformed ? _transformLast : _transformStart;
p0 = _getMouseCoords(source);
p1 = t0.invert(p0);
k2 = t0.k * Math.pow(2, -dY / 500);
k2 = clamp(k2, kMin, kMax);
x2 = p0[0] - p1[0] * k2;
y2 = p0[1] - p1[1] * k2;
// 2 finger map panning (Mac only, all browsers except Firefox #8595) - #5492, #5512
// Panning via the `wheel` event will always have:
// - `ctrlKey = false`
// - `deltaX`,`deltaY` are round integer pixels
} else if (detected.os === 'mac' && detected.browser !== 'Firefox' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
p1 = projection.translate();
x2 = p1[0] - dX;
y2 = p1[1] - dY;
k2 = projection.scale();
k2 = clamp(k2, kMin, kMax);
}
// something changed - replace the event transform
if (x2 !== x || y2 !== y || k2 !== k) {
x = x2;
y = y2;
k = k2;
eventTransform = d3_zoomIdentity.translate(x2, y2).scale(k2);
if (_zoomerPanner._transform) {
// utilZoomPan interface
_zoomerPanner._transform(eventTransform);
} else {
// d3_zoom interface
_selection.node().__zoom = eventTransform;
}
}
}
if (_transformStart.x === x &&
_transformStart.y === y &&
_transformStart.k === k) {
return; // no change
}
if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
surface.interrupt();
dispatch.call('hitMinZoom', this, map);
setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
scheduleRedraw();
dispatch.call('move', this, map);
return;
}
projection.transform(eventTransform);
var withinEditableZoom = map.withinEditableZoom();
if (_lastWithinEditableZoom !== withinEditableZoom) {
if (_lastWithinEditableZoom !== undefined) {
// notify that the map zoomed in or out over the editable zoom threshold
dispatch.call('crossEditableZoom', this, withinEditableZoom);
}
_lastWithinEditableZoom = withinEditableZoom;
}
var scale = k / _transformStart.k;
var tX = (x / scale - _transformStart.x) * scale;
var tY = (y / scale - _transformStart.y) * scale;
if (context.inIntro()) {
curtainProjection.transform({
x: x - tX,
y: y - tY,
k: k
});
}
if (source) {
_lastPointerEvent = event;
}
_isTransformed = true;
_transformLast = eventTransform;
utilSetTransform(supersurface, tX, tY, scale);
scheduleRedraw();
dispatch.call('move', this, map);
function isInteger(val) {
return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
}
}