Google Maps v3: как определить, что плитки наложения ImageMapType закончили загрузку?

Я работаю с API 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 завершена?

РЕДАКТИРОВАТЬ

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

А еще котята.


person David Mills    schedule 07.09.2011    source источник


Ответы (3)


Казалось бы, не существует «стандартного» способа узнать, когда наложение ImageMapType завершило загрузку, но благодаря предложение Мартина на Форумы Google Maps API v3 Мне удалось добавить собственное пользовательское событие, которое генерируется после завершения загрузки слоя.

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

  • Каждый раз, когда запрашивается URL-адрес, добавляйте URL-адрес в список ожидающих URL-адресов.
  • Переопределите ImageMapType.getTile(), чтобы мы могли добавить прослушиватели событий «onload» к каждому элементу ‹img›.
  • Когда срабатывает событие загрузки каждого изображения, удалите это изображение из списка ожидающих 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);
});
person David Mills    schedule 08.09.2011
comment
Спасибо за отличное решение! - person mfras3r; 14.03.2013
comment
Кажется, что это не работает (уже?). Даже в вашем jsfiddle он никогда не регистрируется. Оверлей простаивает, хотя все изображения котенка загружены. - person Tarmo; 21.11.2019

Используйте событие tilesloaded

Документ: О загруженных фрагментах событий

введите здесь описание изображения

person Mateus Moura    schedule 08.06.2021

Основываясь на ответе @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);

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

person Jose Gómez    schedule 12.04.2015
comment
Единственная проблема, которую я заметил в этой версии, заключается в том, что событие onload не запускается, когда плитки уже находятся в кеше. Решение должно состоять в том, чтобы установить событие onload перед параметром src (stackoverflow.com/a/12355031/1816603), но Я не уверен, совместимо ли это с подходом переопределения метода getTile, поскольку источник изображения установлен в коде Карт. - person Jose Gómez; 12.04.2015