简体   繁体   中英

Openlayers3 - fractional zoom on touch device using pinch gesture?

Openlayers3 can display a map at a fractional zoom level programmatically by specifying a fractional number in setZoom() .

However, I want my users to be able to get a fractional zoom level on a mobile touch-driven device (ie, a smart phone). Pinch or reverse pinch zooms out/in by jumping to the nearest whole zoom level when the fingers are removed from the screen.

How can a touch/mobile user get Openlayers3 to stay at the exact extent (fractional zoom level) that the user has pinched to?

Is there something I can add to the javascript for my Openlayers3 map or view to get this to work as desired?

In particular, I note that Openlayers3 has a ol.interaction.PinchZoom() class ( https://openlayers.org/en/latest/apidoc/ol.interaction.PinchZoom.html ). This is a subclass of ol.interaction.Pointer which has a handleUpEvent and a handleEvent (which should be a function). So in theory, I should be able to either replace the handleUpEvent in PinchZoom OR replace the default PinchZoom interaction with one that has a custom event handler function. But with both of these approaches, I can't get my new handleUpEvent function to be called.

(While trawling the OL code for PinchZoom I have found that the default PinchZoom does what I want if one finger is lifted from the touch-screen before the other finger, but I still want to get this working when both fingers are lifted simultaneously.)

Here is what I've tried so far...

FIRST ATTEMPT - This just attempts to replace the standard PinchZoom 's handleUpEvent with a custom one, and set this as the only interaction for the map. However, the event function is never called (never logs anything).

function handleUpEvent(evt) {
    console.log("Up event handler");
    return true; //  Stop drag
}

map = new ol.Map({
    layers: [],
    target: 'map',
    controls: controls,
    interactions: [new ol.interaction.PinchZoom({handleEvent: handleUpEvent})],
    view: new ol.View({projection: projCode})
});

SECOND ATTEMPT - This attempt is based on the actual OL code for creating the standard PinchZoom interaction. In this case, all my event handler functions DO get called, the number of touches ( targetPointers ) is always zero (as logged). I'm no javascript guru, but I suspect that this is because the symbols in ol.js are different to in ol-debug.js which I'm basing this on. In fact I had to declare targetPointers myself to even get this to run, even though it is declared by OL itself already (but presumably using a different symbol name in the non-debug version).

function handleDownEvent(mapBrowserEvent) {
    console.log("DOWN event handler");
    this.anchor_ = null;
    this.lastDistance_ = undefined;
    this.lastScaleDelta_ = 1;
    mapBrowserEvent.map.render();
    return true;  //  Start drag
}

function handleDragEvent(mapBrowserEvent) {
    if ( this.targetPointers.length < 2 ) {
        console.log("DRAG event ignored - touches ", this.targetPointers.length);
    } else {
        console.log("DRAG event handled");
        var scaleDelta = 1.0;
        var touch0 = this.targetPointers[0];
        var touch1 = this.targetPointers[1];
        var dx = touch0.clientX - touch1.clientX;
        var dy = touch0.clientY - touch1.clientY;

        // distance between touches
        var distance = Math.sqrt(dx * dx + dy * dy);

        if (this.lastDistance_ !== undefined) {
            scaleDelta = this.lastDistance_ / distance;
        }
        this.lastDistance_ = distance;
        if (scaleDelta != 1.0) {
            this.lastScaleDelta_ = scaleDelta;
        }

        var map = mapBrowserEvent.map;
        var view = map.getView();
        var resolution = view.getResolution();

        // scale anchor point.
        var viewportPosition = map.getViewport().getBoundingClientRect();
        var centroid = ol.interaction.Pointer.centroid(this.targetPointers);
        centroid[0] -= viewportPosition.left;
        centroid[1] -= viewportPosition.top;
        this.anchor_ = map.getCoordinateFromPixel(centroid);

        // scale, bypass the resolution constraint
        map.render();
        ol.interaction.Interaction.zoomWithoutConstraints(
            map, view, resolution * scaleDelta, this.anchor_);
    }
}

function handleUpEvent(mapBrowserEvent) {
    console.log("UP event handler");
    return true;  //  Stop drag
}

var pinchZoom = new ol.interaction.Pointer({
        handleDownEvent: handleDownEvent,
        handleDragEvent: handleDragEvent,
        handleUpEvent: handleUpEvent
        });

pinchZoom.targetPointers = [];

map = new ol.Map({
    interactions: ol.interaction.defaults().extend([pinchZoom]),
    layers: [
             new ol.layer.Tile({
                               source: new ol.source.TileJSON({url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure'})
                               })
             ],
    target: 'map',
    view: new ol.View({center: [0, 0], zoom: 3})
});

The problems with your attempts:

First attempt

The option handleEvent for ol.interaction.PinchZoom does not exists. That is why your callback is not executed.

Second attempt

As you already noted you try to use openlayers internals. This might work with the debug build but will not work in the release build as the names are different than.

Solution

Add the option to keep a fractional resolution at the pinch zoom end in the openlayers sources.

Issue: https://github.com/openlayers/ol3/issues/6223

Pull request: https://github.com/openlayers/ol3/issues/6224

The pinch zoom will by default keep the fractional zoom level as chosen by the user .. starting with OpenLayers 3.20


An earlier idea:

As an example I added the option keepFractionalZoomLevel to ol.interaction.PinchZoom : https://github.com/aAXEe/ol3/commit/7639cb20d17858492652896bcd4a6ff7992a9bb0

See this fiddle for a working example: https://jsfiddle.net/wm78prro/5/

Note: The fiddle uses a custom openlayers build: https://github.com/aAXEe/ol3/pull/1
The deploy url is: https://deploy-preview-1--pump-attendant-rhinoceros-42285.netlify.com/ol-debug.js

Does this example behave as you want?
As this may be usefull for others we can try to integrate it into openlayers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM