Google地图v3:如何判断ImageMapType叠加层的瓦片何时加载完成?

8
我正在使用Google Maps v3 API,并且有一个基于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/ - 观察控制台输出单词“idled”,以查看何时触发空闲事件。请注意,当您单击添加覆盖层的按钮时,它永远不会触发。
另外,小猫咪。
3个回答

10

看起来似乎没有一种“开箱即用”的方法可以知道ImageMapType叠加层何时完成加载,但感谢Google Maps API v3论坛上的Martin建议,我能够添加自己的自定义事件,当图层完成加载时会发射该事件。

基本方法如下:

  • 每次请求URL时,将URL添加到待处理URL列表中
  • 覆盖ImageMapType.getTile()以便我们可以向每个<img>元素添加“onload”事件监听器。
  • 当每个图像的“load”事件触发时,从待处理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
似乎这个不再起作用了?即使在你的jsfiddle中,即使所有小猫图片都已加载完毕,它也从未记录“覆盖层处于空闲状态”。 - Tarmo

3

使用事件 tilesloaded

文档:关于事件 tilesloaded

enter image description here

示例:

let layer = new window.google.maps.ImageMapType({
    getTileUrl: getTileUrl,
    tileSize: new window.google.maps.Size(256, 256),
    minZoom: 0,
    maxZoom: 24,
    opacity: 1.0,
    isPng: true,
});

map.overlayMapTypes.setAt(0, layer);
layer.addListener("tilesloaded", () => {
    console.log("Overlay tiles loaded");
});

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);

额外功能:支持多层和超时(以防服务器慢或不稳定)。

我在这个版本中观察到的唯一问题是当瓦片已经在缓存中时,onload事件不会被触发。解决方案应该是在src参数之前设置onload事件(https://dev59.com/-Gct5IYBdhLWcg3wF5pF#12355031),但我不确定这是否与覆盖getTile方法的方法兼容,因为图像src是在Maps代码中设置的。 - Jose Gómez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接