openlayers: Pinch zoom crashes the touch control in slow/old mobile browsers (especially iOS)

Describe the bug Pinch zooming a page when the renderer/browser gets busy, puts the map in a state where zooming no longer works and panning is woking “in half speed”. May be the same issue as with other touch problems where “an invisible finger” is stuck “on” and single finger pan is actually zoom with very wide finger distance.

Symptoms are somewhat similar to #11251.

To Reproduce Steps to reproduce the behavior:

  1. Using iOS 12 device zoom in and out using the attached test code
  2. After a while the zooming gestures no longer work
  3. The only way to recover is to reload the page

iOS crash happens both in Safari and Chrome, so the issue might be renderer related.

Expected behavior Two finger zooming does not crash the map.

Sample code: index.html:

<!DOCTYPE html>
<html lang="en" style="height: 100%;">
  <head>
    <title>Simple Map</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <style>
      .map {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body style="height: 100%;">
    <div id="map" class="map"></div>
    <script src="main.js"></script>
  </body>
</html>

main.js

import 'ol/ol.css';
import Map from 'ol/Map';
import OSM from 'ol/source/OSM';
import TileLayer from 'ol/layer/Tile';
import View from 'ol/View';
import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
import OSMXML from 'ol/format/OSMXML';
import { transformExtent } from 'ol/proj';
import { defaults as defaultControls, ScaleLine, ZoomSlider } from 'ol/control';
import { fromLonLat } from 'ol/proj';



const overpass_server = "https://overpass.kumi.systems/api/interpreter"



const nameStyle = new Style({
    text: new Text({
      font: 'bold 20px "Open Sans", "Arial Unicode MS", "sans-serif"',
      placement: 'line',
      fill: new Fill({
        color: 'white'
      }),
      stroke: new Stroke({ color: 'grey', width: 3 })
    })
  });

var streetNamesOSM = new VectorTileSource({
    format: new OSMXML(),
    attributions: "-- Streetnames: OpenStreetMap",
    url: overpass_server + '/{z}/{x}/{-y}',
    maxZoom: 13,
    minZoom: 13,
    tileLoadFunction: function (tile, url) {
        console.debug("kadunnimet url " + url)

        tile.setLoader(function (extent, resolution, projection) {
            var epsg4326Extent = transformExtent(extent, projection, 'EPSG:4326');
            var query = '[bbox: ' +
                epsg4326Extent[1] + ',' + epsg4326Extent[0] + ',' +
                epsg4326Extent[3] + ',' + epsg4326Extent[2] +
                '];(way[highway="residential"]["name"];way[highway="primary"]["name"];way[highway="secondary"]["name"];way[highway="tertiary"]["name"];way[highway="unclassified"]["name"];);(._;>;);out;';

            fetch(overpass_server + '?data=' + encodeURI(query))
                .then(
                    function (response) {
                        if (response.status !== 200) {
                            console.log('Looks like there was a problem getting OSM features. Status Code: ' +
                                response.status);
                            return;
                        }

                        // Examine the text in the response
                        response.text().then(function (data) {
                            //console.log(data);
                            var features = new OSMXML().readFeatures(data, {
                                featureProjection: map.getView().getProjection()
                            });
                            tile.setFeatures(features);
                        });
                    }
                )
                .catch(function (err) {
                    console.log('Fetch Error :-S', err);
                });
        }
        )
    }
});

var nimiOSMLayer = new VectorTileLayer({
    name: "OSMstreetNames",
    source: streetNamesOSM,
    minZoom: 15.6,
    renderMode: 'hybrid',
    renderOrder: null,
    renderBuffer: 100,
    style: function (feature) {
        if (['residential', 'primary', 'secondary', 'tertiary', 'unclassified'].includes(feature.get('highway'))) {
            nameStyle.getText().setText(feature.get('name'));
            return nameStyle;
        }
        else {
            return null
        }
    }
});

var map = new Map({
    layers: [
        new TileLayer({
            source: new OSM(),
        }), nimiOSMLayer],
    target: 'map',
    view: new View({
        center: fromLonLat([-0.12, 51.50]),
        zoom: 16,
    }),
    controls: defaultControls({ zoomOptions: { delta: 0.5 } })
});

package.json

{
    "name": "simple",
    "dependencies": {
      "ol": "6.4.3"
    },
    "devDependencies": {
      "parcel": "1.11.0"
    },
    "scripts": {
      "start": "parcel index.html --host 0.0.0.0",
      "build": "parcel build --experimental-scope-hoisting --public-url . index.html"
    }
  }

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 43 (16 by maintainers)

Most upvoted comments

I have the same problem using map with 100 markers. New Android devices crash after pan/zoom, while old devices don’t.

Using pixelRatio: 1 solves the problem

Now what if you go into Safari settings - Experimental features, and enable Pointer Events?

I need to learn to use codesandbox first…

It looks like the code you pasted is from a modified example. If that’s the case, just click “Edit” on the top right of the example, and you’ll be taken to a codesandbox with the code of the example.