Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotate mobile device path don't follow #43

Open
ttooo opened this issue Nov 19, 2023 · 5 comments · May be fixed by #61
Open

Rotate mobile device path don't follow #43

ttooo opened this issue Nov 19, 2023 · 5 comments · May be fixed by #61

Comments

@ttooo
Copy link

ttooo commented Nov 19, 2023

I searched the web a lot to figure out if there was a solution and I checked your code too.

The issue is that on map rotate with fingers the path don't follow, you can try with your demo.

Here I try your sample on an iPhone 12 Pro Max (iOS 17.0.3 on Safari). I pointed with a white arrow the path, as you can see during rotation the path don't follow, it moves on its own. It's when I stop touching the map the path come back at its place.

The markers always remain at the correct spot. Not the path, polylines, etc... I don't know how to fix them. When I realeased my fingers there are always at the good spot.

rotation

Here is an other sample with my code, it does the same, but it's maybe more visible since it's more zoomed.

rotation2

Thanks a lot for your plugin it helps me a lot for my project. I just miss this feature I can't fix.

@Raruto
Copy link
Owner

Raruto commented Nov 22, 2023

during rotation the path don't follow, it moves on its own.

It could also be that it simply doesn't rotate.

Or it could be also related due to an "un-fired" event (leaflet-rotate → leaflet) or an "un-listened" event (leaflet-rotate ← leaflet).

The relevant touch handler file should be the following:

/**
* TouchGestures is both TouchZoom plus TouchRotate
*
* @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
* @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
*
* @typedef L.Map.TouchGestures
*/
L.Map.mergeOptions({
/**
* Set it to false if you don't want the map to
* zoom beyond min/max zoom and then bounce back
* when pinch-zooming.
*
* @type {Boolean}
*/
bounceAtZoomLimits: true,
});
L.Map.TouchGestures = L.Handler.extend({
initialize: function(map) {
this._map = map;
this.rotate = !!this._map.options.touchRotate;
this.zoom = !!this._map.options.touchZoom;
},
addHooks: function() {
L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
},
removeHooks: function() {
L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
},
_onTouchStart: function(e) {
var map = this._map;
if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) { return; }
var p1 = map.mouseEventToContainerPoint(e.touches[0]),
p2 = map.mouseEventToContainerPoint(e.touches[1]),
vector = p1.subtract(p2);
this._centerPoint = map.getSize()._divideBy(2);
this._startLatLng = map.containerPointToLatLng(this._centerPoint);
if (this.zoom) {
if (map.options.touchZoom !== 'center') {
this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
}
this._startDist = p1.distanceTo(p2);
this._startZoom = map.getZoom();
this._zooming = true;
} else {
this._zooming = false;
}
if (this.rotate) {
this._startTheta = Math.atan(vector.x / vector.y);
this._startBearing = map.getBearing();
if (vector.y < 0) { this._startBearing += 180; }
this._rotating = true;
} else {
this._rotating = false;
}
this._moved = false;
map._stop();
L.DomEvent
.on(document, 'touchmove', this._onTouchMove, this)
.on(document, 'touchend touchcancel', this._onTouchEnd, this);
L.DomEvent.preventDefault(e);
},
_onTouchMove: function(e) {
if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; }
var map = this._map,
p1 = map.mouseEventToContainerPoint(e.touches[0]),
p2 = map.mouseEventToContainerPoint(e.touches[1]),
vector = p1.subtract(p2),
scale = p1.distanceTo(p2) / this._startDist,
delta;
if (this._rotating) {
var theta = Math.atan(vector.x / vector.y);
var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG;
if (vector.y < 0) { bearingDelta += 180; }
if (bearingDelta) {
/**
* @TODO the pivot should be the last touch point,
* but zoomAnimation manages to overwrite the rotate
* pane position. Maybe related to #3529.
*
* @see https://github.com/Leaflet/Leaflet/pull/3529
* @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
*/
map.setBearing(this._startBearing - bearingDelta);
}
}
if (this._zooming) {
this._zoom = map.getScaleZoom(scale, this._startZoom);
if (!map.options.bounceAtZoomLimits && (
(this._zoom < map.getMinZoom() && scale < 1) ||
(this._zoom > map.getMaxZoom() && scale > 1))) {
this._zoom = map._limitZoom(this._zoom);
}
if (map.options.touchZoom === 'center') {
this._center = this._startLatLng;
if (scale === 1) { return; }
} else {
// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD;
this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha)));
}
}
if (!this._moved) {
map._moveStart(true, false);
this._moved = true;
}
L.Util.cancelAnimFrame(this._animRequest);
var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
L.DomEvent.preventDefault(e);
},
_onTouchEnd: function() {
if (!this._moved || !(this._zooming || this._rotating)) {
this._zooming = false;
return;
}
this._zooming = false;
this._rotating = false;
L.Util.cancelAnimFrame(this._animRequest);
L.DomEvent
.off(document, 'touchmove', this._onTouchMove, this)
.off(document, 'touchend touchcancel', this._onTouchEnd, this);
if (this.zoom) {
// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
if (this._map.options.zoomAnimation) {
this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
} else {
this._map._resetView(this._center, this._map._limitZoom(this._zoom));
}
}
},
});
/**
* Add Touch Gestures handler (enabled unless `touchGestures` is unset).
*
* @property {L.Map.TouchGestures} touchGestures
*/
L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);

but you should also check core leaflet repo (and everything related to the L.Map class) to get a better idea.

👋 Raruto

@SuNNjek
Copy link

SuNNjek commented Dec 14, 2023

I have the same issue on Chrome on Android. Would be nice if this could be fixed because it makes zooming/rotating with lots of polygons on the screen pretty confusing to look at.

@mauriciabad
Copy link

I'm experiencing the same. In fact, in the demo it also happens if you just zoom out a lot using desktop.

Initially I only had markers, and it was fine. But now that I have paths, the map looks broken while rotating. :(

@rphlo
Copy link
Contributor

rphlo commented Nov 3, 2024

Hi, for those who need a fix for that, adding this block of code inside _onTouchMovein the code of leaflet-rotate/src/map/handler/TouchGestures.js solved it for me.

Might create a PR if @Raruto think it is ok.

if (this.zoom) {
    if (this._map.options.zoomAnimation) {
        this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
    } else {
        this._map._resetView(this._center, this._map._limitZoom(this._zoom));
    }
}

@rphlo rphlo linked a pull request Nov 4, 2024 that will close this issue
@Raruto
Copy link
Owner

Raruto commented Nov 26, 2024

@mauriciabad @ttooo @SuNNjek do you confirm this solution? #61

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants