Skip to content

Commit

Permalink
Update TouchZoomRotate handler to use render-loop-driven camera
Browse files Browse the repository at this point in the history
  • Loading branch information
Anand Thakker authored and jfirebaugh committed Feb 26, 2018
1 parent 052c020 commit 8ac706f
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 29 deletions.
94 changes: 66 additions & 28 deletions src/ui/handler/touch_zoom_rotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ const DOM = require('../../util/dom');
const util = require('../../util/util');
const window = require('../../util/window');
const browser = require('../../util/browser');
const {Event} = require('../../util/evented');

import type Map from '../map';
import type Point from '@mapbox/point-geometry';
import type Transform from '../../geo/transform';

const inertiaLinearity = 0.15,
inertiaEasing = util.bezier(0, 0, inertiaLinearity, 1),
Expand All @@ -30,6 +32,7 @@ class TouchZoomRotateHandler {
_startBearing: number;
_gestureIntent: 'rotate' | 'zoom' | void;
_inertia: Array<[number, number, Point]>;
_lastTouchEvent: TouchEvent;

/**
* @private
Expand All @@ -40,7 +43,8 @@ class TouchZoomRotateHandler {

util.bindAll([
'_onMove',
'_onEnd'
'_onEnd',
'_onTouchFrame'
], this);
}

Expand Down Expand Up @@ -113,25 +117,30 @@ class TouchZoomRotateHandler {
p1 = DOM.mousePos(this._el, e.touches[1]);

this._startVec = p0.sub(p1);
this._startScale = this._map.transform.scale;
this._startBearing = this._map.transform.bearing;
this._gestureIntent = undefined;
this._inertia = [];

window.document.addEventListener('touchmove', this._onMove, false);
window.document.addEventListener('touchend', this._onEnd, false);
}

_getTouchEventData(e: TouchEvent) {
const p0 = DOM.mousePos(this._el, e.touches[0]),
p1 = DOM.mousePos(this._el, e.touches[1]);

const vec = p0.sub(p1);
return {
vec,
center: p0.add(p1).div(2),
scale: vec.mag() / this._startVec.mag(),
bearing: this._rotationDisabled ? 0 : vec.angleWith(this._startVec) * 180 / Math.PI
};
}

_onMove(e: TouchEvent) {
if (e.touches.length !== 2) return;

const p0 = DOM.mousePos(this._el, e.touches[0]),
p1 = DOM.mousePos(this._el, e.touches[1]),
p = p0.add(p1).div(2),
vec = p0.sub(p1),
scale = vec.mag() / this._startVec.mag(),
bearing = this._rotationDisabled ? 0 : vec.angleWith(this._startVec) * 180 / Math.PI,
map = this._map;
const {vec, scale, bearing} = this._getTouchEventData(e);

// Determine 'intent' by whichever threshold is surpassed first,
// then keep that state for the duration of this gesture.
Expand All @@ -146,34 +155,62 @@ class TouchZoomRotateHandler {
}

if (this._gestureIntent) {
this._map.fire(new Event(`${this._gestureIntent}start`, { originalEvent: e }));
this._map.fire(new Event('movestart', { originalEvent: e }));
this._startVec = vec;
this._startScale = map.transform.scale;
this._startBearing = map.transform.bearing;
}
}

} else {
const param: Object = { duration: 0, around: map.unproject(p) };
this._lastTouchEvent = e;
this._map._startAnimation(this._onTouchFrame);

if (this._gestureIntent === 'rotate') {
param.bearing = this._startBearing + bearing;
}
if (this._gestureIntent === 'zoom' || this._gestureIntent === 'rotate') {
param.zoom = map.transform.scaleZoom(this._startScale * scale);
}
e.preventDefault();
}

map.stop();
this._drainInertiaBuffer();
this._inertia.push([browser.now(), scale, p]);
_onTouchFrame(tr: Transform) {
const gestureIntent = this._gestureIntent;
if (!gestureIntent) return;

map.easeTo(param, { originalEvent: e });
if (!this._startScale) {
this._startScale = tr.scale;
this._startBearing = tr.bearing;
}

e.preventDefault();
const {center, bearing, scale} = this._getTouchEventData(this._lastTouchEvent);
const around = tr.pointLocation(center);
const aroundPoint = tr.locationPoint(around);

if (gestureIntent === 'rotate') {
tr.bearing = this._startBearing + bearing;
}

tr.zoom = tr.scaleZoom(this._startScale * scale);

tr.setLocationAtPoint(around, aroundPoint);

this._map.fire(new Event(gestureIntent, {originalEvent: this._lastTouchEvent}));
this._map.fire(new Event('move', {originalEvent: this._lastTouchEvent}));

this._drainInertiaBuffer();
this._inertia.push([browser.now(), scale, center]);
}

_onEnd(e: TouchEvent) {
window.document.removeEventListener('touchmove', this._onMove);
window.document.removeEventListener('touchend', this._onEnd);

const gestureIntent = this._gestureIntent;
const startScale = this._startScale;

delete this._gestureIntent;
delete this._startScale;
delete this._startBearing;
delete this._lastTouchEvent;

if (!gestureIntent) return;

this._map.fire(new Event(`${gestureIntent}end`, { originalEvent: e }));

this._drainInertiaBuffer();

const inertia = this._inertia,
Expand All @@ -186,8 +223,8 @@ class TouchZoomRotateHandler {

const last = inertia[inertia.length - 1],
first = inertia[0],
lastScale = map.transform.scaleZoom(this._startScale * last[1]),
firstScale = map.transform.scaleZoom(this._startScale * first[1]),
lastScale = map.transform.scaleZoom(startScale * last[1]),
firstScale = map.transform.scaleZoom(startScale * first[1]),
scaleOffset = lastScale - firstScale,
scaleDuration = (last[0] - first[0]) / 1000,
p = last[2];
Expand Down Expand Up @@ -219,7 +256,8 @@ class TouchZoomRotateHandler {
zoom: targetScale,
duration: duration,
easing: inertiaEasing,
around: this._aroundCenter ? map.getCenter() : map.unproject(p)
around: this._aroundCenter ? map.getCenter() : map.unproject(p),
noMoveStart: true
}, { originalEvent: e });
}

Expand Down
2 changes: 1 addition & 1 deletion test/unit/ui/handler/touch_zoom_rotate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function createMap() {
return new Map({ container: DOM.create('div', '', window.document.body) });
}

test('TouchZoomRotateHandler does not begin a box zoom if preventDefault is called on the touchstart event', (t) => {
test('TouchZoomRotateHandler does not begin a gesture if preventDefault is called on the touchstart event', (t) => {
const map = createMap();

map.on('touchstart', e => e.preventDefault());
Expand Down

0 comments on commit 8ac706f

Please sign in to comment.