Карты Google v3. Как определить, когда плитки оверлея ImageMapType заканчивают загрузку? - PullRequest
7 голосов
/ 08 сентября 2011

Я работаю с 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 закончил загрузку?

EDIT

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

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

Ответы [ 2 ]

10 голосов
/ 09 сентября 2011

Казалось бы, нет "готового" способа узнать, когда оверлей ImageMapType закончил загрузку, но благодаря предложению от Мартина на Google Maps API v3 Forums Мне удалось добавить собственное событие, которое генерируется после завершения загрузки слоя.

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

  • Каждый раз, когда запрашивается URL-адрес, добавьте его в список ожидающих URL-адресов
  • Переопределите ImageMapType.getTile (), чтобы мы могли добавлять прослушиватели событий «onload» к каждому элементу image.
  • Когда срабатывает событие загрузки каждого изображения, удалите это изображение из списка ожидающих 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);
});
0 голосов
/ 12 апреля 2015

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

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

...