2011-09-07 1 views
7

Я работаю с API Google Maps v3, и у меня есть пользовательский слой наложения, основанный на классе ImageMapType. Я бы хотел показать какой-то показатель загрузки, в то время как плитки наложения загружаются, но я не вижу никакого способа узнать, когда они будут закончены.Google Maps v3: Как узнать, когда плитки наложения на ImageMapType закончены?

Код для создания наложения выглядит примерно следующим образом:

var myOverlay = new google.maps.ImageMapType({ 
    getTileUrl: myGetTileUrl, 
    tileSize: new google.maps.Size(256, 256), 
    isPng: true 
}); 

myMap.overlayMapTypes.push(myOverlay); 

Вышеуказанные работы просто отлично, и покрышка успешно загружает; просто кажется, что никакие события не выбрасываются картой, чтобы указать что-либо о статусе оверлея ImageMapType.

Я ожидаю, что карта, по крайней мере, испустит «незанятое» событие, когда плитки закончат загрузку, но насколько я могу сказать, это не так.

Как я могу узнать, когда наложение изображений ImageMapType закончено, загрузка?

EDIT

Я написал тестовый случай на jsFiddle: http://jsfiddle.net/6yvcB/ - Следите консольный вывод для слова «вхолостую», чтобы увидеть, когда простои пожаров событий. Обратите внимание, что он не срабатывает, когда вы нажимаете кнопку, чтобы добавить наложение.

Также котята.

ответ

10

Казалось бы, нет «из коробки» способа узнать, когда ImageMapType оверлей завершила загрузку, но благодаря suggestion from Martin над на Google Maps API v3 Forums я смог добавить в моем собственном пользовательском событии, которое испускается при слой заканчивает загрузку.

Основной подход:

  • Каждый раз, когда URL запрашивается, добавьте URL в список ожидающих адресов
  • Override ImageMapType.getTile(), так что мы можем добавить «OnLoad» слушателей событий к каждому <img> элемент.
  • При возникновении события «загрузка» каждого изображения удалите это изображение из списка ожидающих URL-адресов.
  • Когда список ожидающих URL-адресов пуст, выпустите наше пользовательское событие «оверлей-простоя».

Я скопировал код ниже для потомков, но вы можете увидеть его в действии на jsFiddle: http://jsfiddle.net/6yvcB/22/

// Create a base map 
var options = { 
    zoom: 3, 
    center: new google.maps.LatLng(37.59, -99.13), 
    mapTypeId: "terrain" 
}; 
var map = new google.maps.Map($("#map")[0], options); 

// Listen for the map to emit "idle" events 
google.maps.event.addListener(map, "idle", function(){ 
    console.log("map is idle"); 
}); 

// Keep track of pending tile requests 
var pendingUrls = []; 

$("#btn").click(function() { 
    var index = 0; 
    var urls = [ "http://placekitten.com/256/256", 
       "http://placekitten.com/g/256/256", 
       "http://placekitten.com/255/255", 
       "http://placekitten.com/g/255/255", 
       "http://placekitten.com/257/257", 
       "http://placekitten.com/g/257/257" ]; 

    var overlay = new google.maps.ImageMapType({ 
     getTileUrl: function() { 
      var url = urls[index % urls.length]; 
      index++; 

      // Add this url to our list of pending urls 
      pendingUrls.push(url); 

      // if this is our first pending tile, signal that we just became busy 
      if (pendingUrls.length === 1) { 
       $(overlay).trigger("overlay-busy"); 
      } 

      return url; 
     }, 
     tileSize: new google.maps.Size(256, 256), 
     isPng: true, 
     opacity: 0.60 
    }); 

    // Listen for our custom events 
    $(overlay).bind("overlay-idle", function() { 
     console.log("overlay is idle"); 
    }); 

    $(overlay).bind("overlay-busy", function() { 
     console.log("overlay is busy"); 
    }); 


    // Copy the original getTile function so we can override it, 
    // but still make use of the original function 
    overlay.baseGetTile = overlay.getTile; 

    // Override getTile so we may add event listeners to know when the images load 
    overlay.getTile = function(tileCoord, zoom, ownerDocument) { 

     // Get the DOM node generated by the out-of-the-box ImageMapType 
     var node = overlay.baseGetTile(tileCoord, zoom, ownerDocument); 

     // Listen for any images within the node to finish loading 
     $("img", node).one("load", function() { 

      // Remove the image from our list of pending urls 
      var index = $.inArray(this.__src__, pendingUrls); 
      pendingUrls.splice(index, 1); 

      // If the pending url list is empty, emit an event to 
      // indicate that the tiles are finished loading 
      if (pendingUrls.length === 0) { 
       $(overlay).trigger("overlay-idle"); 
      } 
     }); 

     return node; 
    }; 

    map.overlayMapTypes.push(overlay); 
}); 
+1

Спасибо за отличное решение! – mfras3r

0

на основе ответа по @David, я создал чистое Javascript альтернативы (особенно учитывая, что Op не указал jQuery).

var pendingUrls = []; 

function addPendingUrl(id, url) 
{ 
    // Add this url to our list of pending urls 
    pendingUrls[id].push(url); 

    //console.log("URL " + url + " added (" + pendingUrls[id].length + ")"); 

    // if this is our first pending tile, signal that we just became busy 
    if (pendingUrls[id].length === 1) { 
     console.log("overlay is busy"); 
    } 
} 

function addTileLoadListener(id, mapType, timeout) 
{ 
    // Initialise the sub-array for this particular id 
    pendingUrls[id] = []; 

    // Copy the original getTile function so we can override it, but still make use of the original function 
    mapType.baseGetTile = mapType.getTile; 

    // Override getTile so we may add event listeners to know when the images load 
    mapType.getTile = function(tileCoord, zoom, ownerDocument) 
    { 
     // Get the DOM node generated by the out-of-the-box ImageMapType 
     var node = mapType.baseGetTile(tileCoord, zoom, ownerDocument); 

     //console.log("URL " + node.firstChild.__src__ + " confirmed (" + pendingUrls[id].length + ")"); 

     function removePendingImg(node, src, result) 
     { 
      var index = pendingUrls[id].indexOf(src); 
      if (index == -1) 
      { 
       //console.log("URL " + src + " " + "not found" + " (" + pendingUrls[id].length + ")"); 
      } 
      else 
      { 
       pendingUrls[id].splice(index, 1); 
       //console.log("URL " + src + " " + result + " (" + pendingUrls[id].length + ")"); 

       // If the pending url list is empty, emit an event to indicate that the tiles are finished loading 
       if (pendingUrls[id].length === 0) { 
        console.log("overlay is idle"); 
       }     
      } 
     } 

     // Listen for any images within the node to finish loading 
     node.getElementsByTagName("img")[0].onload = function() { 
      //console.log("URL " + node.firstChild.src + " maybe loaded (" + node.firstChild.__src__ + ")"); 

      // Check that we have loaded the final image. We detect this because the node.src ends with what is in node.__src__ 
      var str = node.firstChild.src; 
      var suffix = node.firstChild.__src__; 
      if (str.indexOf(suffix, str.length - suffix.length) !== -1) 
      { 
       removePendingImg(node, node.firstChild.__src__, "loaded"); // Remove the image from our list of pending urls 
      } 
     }; 

     // Limit the wait 
     var imgsrc = node.firstChild.__src__; 
     setTimeout(function() { 
      if (node.firstChild) // if the map has already changed and the image is not going to be loaded, the node is destroyed 
      { 
       //var index = pendingUrls[id].indexOf(node.firstChild.__src__); 
       //if (index != -1) 

       // If the image is not loaded yet (node.src changes to the same value as node.firstChild.__src__ when loaded) 
       var str = node.firstChild.src; 
       var suffix = node.firstChild.__src__; 
       if (!(str.indexOf(suffix, str.length - suffix.length) !== -1)) 
       { 
        node.getElementsByTagName("img")[0].onload = null; // Disable the event handler for this node 
        removePendingImg(node, node.firstChild.__src__, "timed out"); // Remove the image from our list of pending urls 
       } 
      } 
      else removePendingImg(node, imgsrc, "discarded"); // Remove the image from our list of pending urls 
     }, timeout); 

     return node; 
    }; 
} 

И эти функции могут быть легко вызваны из любой функции getTileUrl.

myMapType = new google.maps.ImageMapType({ 
    getTileUrl: function(coord, zoom) 
    { 
     var url = '//a.tile.server.com/' + zoom + '/' + coord.x + '/' + coord.y + '.png'; 

     // Add this url to our list of pending urls, and enable the loading image if appropriate 
     addPendingUrl("myLayer", url); 

     return url; 
    }, 
    tileSize: new google.maps.Size(256, 256), 
    opacity: 0.5 
}); 

// Listen for all the images having been loaded 
addTileLoadListener("myLayer", myMapType, 15000); 

Особенности бонуса: поддержка нескольких уровней и тайм-аутов (в случае, если сервер медленный или неаккуратный).

+0

Единственная проблема, которую я наблюдал в этой версии, заключается в том, что событие onload не запускается, когда плитки уже находятся в кеше.Решение должно состоять в том, чтобы установить событие onload перед параметром src (http://stackoverflow.com/a/12355031/1816603), но я не уверен, совместим ли это с подходом к переопределению метода getTile, поскольку изображение src устанавливается в коде Карты. –

Смежные вопросы