2011-09-07 12 views
7

Estoy trabajando con la API de Google Maps v3, y tengo una capa de superposición personalizada basada en la clase ImageMapType. Me gustaría mostrar un indicador de carga de algún tipo mientras se cargan los mosaicos de la superposición, pero no veo ninguna forma de saber cuándo terminaron.Google Maps v3: ¿Cómo saber cuándo terminan de cargarse las fichas de una superposición ImageMapType?

El código para crear la plantilla tiene un aspecto similar al siguiente:

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

myMap.overlayMapTypes.push(myOverlay); 

lo anterior funciona muy bien, y la superposición de cargar correctamente; simplemente parece que el mapa no emite ningún evento que indique algo sobre el estado de la superposición de ImageMapType.

Esperaría que el mapa al menos emita un evento "inactivo" cuando las baldosas terminan de cargarse, pero hasta donde yo sé no es así.

¿Cómo puedo saber cuándo se ha terminado de cargar la superposición ImageMapType?

EDITAR

escribí un caso de prueba en jsFiddle: http://jsfiddle.net/6yvcB/ - Observe a su salida de la consola para la palabra "plantas ociosas" para ver cuando se activa el evento de inactividad. Tenga en cuenta que nunca se dispara al hacer clic en el botón para agregar una superposición.

También, gatitos.

Respuesta

10

Parecería que no hay manera "fuera de la caja" para saber cuando una superposición ImageMapType ha terminado de cargar, pero gracias a un suggestion from Martin sobre el Google Maps API v3 Forums pude añadir en mi propio evento a medida que se emite cuando el la capa termina de cargarse

El enfoque básico es:

  • Cada vez que se solicita una dirección URL, añadir la URL a una lista de URL pendientes
  • ImageMapType.getTile Ignorar() para que podamos añadir detectores de eventos "onload" para cada elemento <img>.
  • Cuando se activa el evento de "carga" de cada imagen, elimine esa imagen de la lista de URL pendientes.
  • Cuando la lista de URL pendientes está vacía, entonces emite nuestro evento personalizado "superposición inactiva".

He copiado el código de abajo para la posteridad, pero se puede ver en acción en 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

¡Gracias por la gran solución! – mfras3r

0

Sobre la base de la respuesta de @ David, he creado una alternativa Javascript puro (especialmente teniendo en cuenta que Op no especificó 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; 
    }; 
} 

Y estas funciones se pueden invocar fácilmente desde cualquier función 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); 

Características adicionales: soporte para múltiples capas y tiempos de espera (en caso de que el servidor sea lento o descuidado).

+0

El único problema que he observado en esta versión es que el evento de carga no se desencadena cuando las teselas ya están en la memoria caché.La solución debería ser establecer el evento onload antes del parámetro src (http://stackoverflow.com/a/12355031/1816603), pero no estoy seguro de si esto es compatible con el enfoque de anular el método getTet, ya que la imagen src se establece en el código de Maps. –