2011-09-07 18 views
7

我正在使用Google Maps v3 API,並且我有一個基於ImageMapType類的自定義覆蓋圖層。我想在疊加的瓷磚加載時顯示某種加載指示符,但我沒有看到任何方法知道它們何時完成。谷歌地圖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覆蓋圖何時完成加載?

編輯

我寫了一個測試案例上的jsfiddle:http://jsfiddle.net/6yvcB/ - 觀察你的字控制檯輸出「閒置」看到空閒事件觸發時。請注意,點擊按鈕添加疊加層時,它絕不會觸發。

此外,小貓。

回答

10

這似乎沒有「開箱即用」的方式來知道什麼時候ImageMapType覆蓋加載完畢,但由於採用了suggestion from Martin在上Google Maps API v3 Forums我能在自己的自定義事件添加的照射時的層完成加載。

的基本方法是:

  • 每次請求一個URL時,將該網址添加到待處理的URL列表
  • 覆蓋ImageMapType.getTile(),以便我們可以添加「的onload」事件監聽器到每個元素。
  • 當每個圖像的「加載」事件觸發時,從掛起的URL列表中刪除該圖像。
  • 當掛起的URL列表爲空時,發出我們自定義的「overlay-idle」事件。

我已經複製下面爲子孫後代的代碼,但你可以看到它在行動上的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

基於由@大衛的反應,我創建了一個純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

我在這個版本中觀察到的唯一問題是,當tile已經在緩存中時,onload事件不會被觸發。解決方法應該是在src參數(http://stackoverflow.com/a/12355031/1816603)之前設置onload事件,但我不確定這是否與重寫getTile方法的方法兼容,如圖像src在地圖代碼中設置。 –