Jumping overlay when panning on iPhone

69 views
Skip to first unread message

Boris Rarden

unread,
Oct 4, 2011, 7:17:57 PM10/4/11
to google-map...@googlegroups.com
Strange behavior occurs, that can be reproduced upon few tries on iPhone4 Safari (not simulator).  Below is a hosted example of the attached file:


In this example you see a custom blue marker, that is implemented as a custom overlay. When panning into an area where tiles are not yet loaded, sometimes the marker jumps into a weird location.  It appears that that is happening because "draw" is called at an inconsistent state.  The problem is seen after about 5 to 10 panning attempts, into various directions around the marker, usually two viewports away from the original marker.   

This is observable on iPhone4 but not a desktop browser, since iPhone4 renders slower.
test.html

Enoch Lau (Google Employee)

unread,
Oct 4, 2011, 8:38:19 PM10/4/11
to google-map...@googlegroups.com
Thanks for reporting this issue - we'll take a look.

Enoch

Boris Rarden

unread,
Oct 5, 2011, 2:10:58 PM10/5/11
to google-map...@googlegroups.com
I have understood the problem better.  Here's the sequence of events that happens

1. viewport div, lets call it ORIGIN is set to (0,0)  for top left corner of the map
2. marker is created at say (100,100) and shows up inside the viewport
3. user pans east, and as a result ORIGIN is set to (-500,0) for example.  
4. user pans more east, and ORIGIN is set to (-700,0) for example.
5. user pans yet 300 pixel more to the east, and the following happens
    5.1  ORIGIN is set again to (0,0)
    5.2  marker.draw() is called, which computes to be draw at the new position of  (-900,0)

The flicker happens between 5.1 and 5.2, because the marker is still drawn at the old position of (100,100), but the frame of reference already changed.   

Boris Rarden

unread,
Oct 5, 2011, 3:32:53 PM10/5/11
to google-map...@googlegroups.com
Here's my workaround. The attached file is also hosted at:


In the nutshell, the fix detects origin change and dispatches an event called "origin_changed".  Using it:

    map = new google.maps.Map(document.getElementById('mapcanvas'), opts);
    map.setCenter(new google.maps.LatLng(40.75013, -73.987974));
    google.maps.event.addListener(map, 'origin_changed', on_origin_changed);
    new OriginDetector(map);

The implementation:

------8<-------------
  function OriginDetector(map) { 
    this.setMap(map); 
    this.map = map;
    this.reset_();
    var self = this;
    google.maps.event.addListener(map, 'bounds_changed', function(){ self.detect_() });
    google.maps.event.addListener(map, 'zoom_changed', function(){ self.reset_() });
  }

  OriginDetector.prototype = new google.maps.OverlayView();
  OriginDetector.prototype.onAdd = function() { }
  OriginDetector.prototype.onRemove = function() { }

  OriginDetector.prototype.draw = function() { 
    this.detected = false;
  }

  OriginDetector.prototype.reset_ = function(){
    this.testpoint = null;
    this.pos = null;
    this.dispatched = false;
  }

  OriginDetector.prototype.detect_ = function(){
    if (this.detected)
      return;
    if (!this.testpoint) this.testpoint = this.map.getCenter(); 
    var pos = this.getProjection().fromLatLngToDivPixel(this.testpoint);
    if (!this.pos) this.pos = pos;
    if (this.pos.x != pos.x || this.pos.y != pos.y){
      this.testpoint = this.map.getCenter();
      this.pos = this.getProjection().fromLatLngToDivPixel(this.testpoint);
      this.detected = true;
      google.maps.event.trigger(map, "origin_changed");
    }
  }
----->8-------

Then, the rest of the hack is to react on the "origin_changed" event as follows: repeat for all custom overlays (markers) and hide them if they are not in current viewport.  They will be shown on the next call to draw() that will follow after a split second.
test.html
Reply all
Reply to author
Forward
0 new messages