События загрузки

При работе с интерактивными картами значительная часть функциональности зависит от успешной загрузки различных компонентов: самой карты, тайлов, слоёв, геоданных и внешних ресурсов. Для контроля этих процессов в Leaflet предусмотрена развитая система событий загрузки.

События позволяют:

  • определять момент полной готовности карты;
  • отслеживать загрузку тайлов;
  • реагировать на успешное получение данных;
  • обрабатывать ошибки загрузки;
  • отображать индикаторы прогресса;
  • запускать дополнительную логику после завершения инициализации.

Большинство объектов Leaflet наследуются от класса Evented, благодаря чему поддерживают механизм подписки на события через методы:

on()
off()
once()
fire()

Пример подписки:

map.on('load', function () {
    console.log('Карта загружена');
});

Событие load

Событие load является одним из наиболее часто используемых событий загрузки.

Оно возникает после первой успешной инициализации карты и установки начального центра и масштаба.

const map = L.map('map');

map.on('load', function () {
    console.log('Карта готова');
});

map.setView([55.751244, 37.618423], 10);

После выполнения setView() карта получает координаты и уровень масштабирования, вслед за чем генерируется событие load.

Практическое применение

Часто некоторые действия необходимо выполнять только после полной инициализации карты:

map.on('load', function () {
    L.marker([55.751244, 37.618423])
        .addTo(map)
        .bindPopup('Москва');
});

Без ожидания загрузки часть операций может выполняться раньше готовности интерфейса.


Метод whenReady()

Для определения готовности карты существует специальный метод whenReady().

Он выполняет переданную функцию сразу после завершения начальной загрузки.

map.whenReady(function () {
    console.log('Карта полностью готова');
});

Эквивалентное использование через стрелочную функцию:

map.whenReady(() => {
    console.log('Готово');
});

Отличие от события load

Событие:

map.on('load', callback);

и метод:

map.whenReady(callback);

очень похожи, однако whenReady() гарантирует выполнение функции даже в том случае, если карта уже успела загрузиться к моменту вызова метода.

Это делает его особенно удобным при работе с модульным кодом.


Загрузка тайлов

Большинство карт используют тайловые слои (TileLayer).

Каждый слой загружает множество изображений с сервера.

const tiles = L.tileLayer(
    'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
);

Для контроля процесса существуют специальные события.


Событие loading

Событие loading возникает в момент начала загрузки новых тайлов.

tiles.on('loading', function () {
    console.log('Началась загрузка');
});

Событие генерируется:

  • при первой отрисовке карты;
  • при изменении масштаба;
  • при перемещении карты;
  • при обновлении слоя.

Пример отображения индикатора:

tiles.on('loading', function () {
    document.getElementById('loader').style.display = 'block';
});

Событие load для тайлового слоя

После завершения загрузки всех необходимых тайлов генерируется событие load.

tiles.on('load', function () {
    console.log('Все тайлы загружены');
});

Скрытие индикатора загрузки:

tiles.on('load', function () {
    document.getElementById('loader').style.display = 'none';
});

Полный пример:

tiles.on('loading', function () {
    loader.style.display = 'block';
});

tiles.on('load', function () {
    loader.style.display = 'none';
});

Событие tileloadstart

Если требуется контролировать загрузку каждого отдельного тайла, используется событие tileloadstart.

tiles.on('tileloadstart', function (event) {
    console.log('Начало загрузки тайла');
});

Объект события содержит информацию о загружаемом элементе.

tiles.on('tileloadstart', function (event) {
    console.log(event.tile);
});

Свойство tile содержит DOM-элемент изображения.


Событие tileload

После успешной загрузки конкретного тайла возникает событие tileload.

tiles.on('tileload', function (event) {
    console.log('Тайл загружен');
});

Получение адреса изображения:

tiles.on('tileload', function (event) {
    console.log(event.tile.src);
});

Подобный механизм полезен для:

  • диагностики;
  • подсчёта прогресса;
  • логирования;
  • анализа производительности.

Событие tileerror

Во время загрузки могут возникать ошибки.

Например:

  • сервер недоступен;
  • отсутствует изображение;
  • неверный URL;
  • превышены ограничения API.

Для обработки таких ситуаций применяется событие tileerror.

tiles.on('tileerror', function (event) {
    console.error('Ошибка загрузки тайла');
});

Получение информации об ошибке:

tiles.on('tileerror', function (event) {
    console.log(event);
});

Пример замены проблемного изображения:

tiles.on('tileerror', function (event) {
    event.tile.src = 'images/not-found.png';
});

Событие tileabort

Иногда загрузка тайла прерывается ещё до завершения.

Причины:

  • пользователь быстро переместил карту;
  • изменился масштаб;
  • слой был удалён;
  • браузер отменил запрос.

Для таких случаев предусмотрено событие tileabort.

tiles.on('tileabort', function () {
    console.log('Загрузка тайла отменена');
});

Это событие помогает более точно рассчитывать статистику загрузки.


Отслеживание прогресса загрузки тайлов

Нередко требуется показать пользователю процент выполнения загрузки.

Простейшая реализация:

let loaded = 0;
let started = 0;

tiles.on('tileloadstart', function () {
    started++;
});

tiles.on('tileload', function () {
    loaded++;

    const percent =
        Math.round((loaded / started) * 100);

    console.log(percent + '%');
});

Более точный вариант должен учитывать:

  • отменённые тайлы;
  • ошибки загрузки;
  • повторные запросы.

События загрузки GeoJSON

При работе с удалёнными наборами данных часто используется API браузера.

После получения данных создаётся слой GeoJSON.

fetch('/data/cities.geojson')
    .then(response => response.json())
    .then(data => {
        L.geoJSON(data).addTo(map);
    });

Хотя сам объект GeoJSON не выполняет сетевые запросы, событие загрузки можно организовать через завершение запроса.

fetch('/data/cities.geojson')
    .then(response => response.json())
    .then(data => {
        const layer = L.geoJSON(data);

        layer.addTo(map);

        console.log('GeoJSON загружен');
    });

События загрузки изображений

Leaflet предоставляет класс ImageOverlay.

const image = L.imageOverlay(
    'image.png',
    bounds
);

Для него доступны события загрузки изображения.

Событие load

image.on('load', function () {
    console.log('Изображение загружено');
});

После появления изображения на карте обработчик будет вызван автоматически.


Событие error

При невозможности загрузить изображение возникает событие error.

image.on('error', function () {
    console.log('Ошибка загрузки изображения');
});

Пример:

image.on('error', function () {
    alert('Файл недоступен');
});

События загрузки видео

Для видеооверлеев применяется аналогичный механизм.

const video = L.videoOverlay(
    'movie.mp4',
    bounds
);

Отслеживание загрузки:

video.on('load', function () {
    console.log('Видео загружено');
});

Обработка ошибок:

video.on('error', function () {
    console.log('Ошибка загрузки видео');
});

Однократная обработка событий

Иногда событие должно обработаться только один раз.

Для этого используется метод once().

tiles.once('load', function () {
    console.log('Первое завершение загрузки');
});

После выполнения обработчик автоматически удаляется.

Это особенно удобно для:

  • инициализации интерфейса;
  • первого запуска аналитики;
  • одноразовой настройки объектов.

Удаление обработчиков

При динамическом создании слоёв важно своевременно освобождать обработчики.

Удаление конкретной функции:

function onLoaded() {
    console.log('Загружено');
}

tiles.on('load', onLoaded);

tiles.off('load', onLoaded);

Удаление всех обработчиков события:

tiles.off('load');

Удаление всех событий объекта:

tiles.off();

Цепочка событий загрузки тайлов

Типичный жизненный цикл тайлового слоя выглядит следующим образом:

loading
    ↓
tileloadstart
    ↓
tileload
    ↓
tileload
    ↓
tileload
    ↓
load

При ошибках возможны дополнительные события:

loading
    ↓
tileloadstart
    ↓
tileerror
    ↓
load

Либо:

loading
    ↓
tileloadstart
    ↓
tileabort

Понимание этой последовательности позволяет точно проектировать механизмы индикации загрузки и мониторинга состояния карты.


Создание собственного индикатора загрузки

Практический пример:

const loader =
    document.getElementById('loader');

tiles.on('loading', function () {
    loader.classList.add('visible');
});

tiles.on('load', function () {
    loader.classList.remove('visible');
});

CSS:

#loader {
    display: none;
}

#loader.visible {
    display: block;
}

Такой подход широко применяется в веб-ГИС, поскольку загрузка большого количества тайлов или пространственных данных может занимать заметное время.


Комбинирование нескольких источников данных

В сложных приложениях карта может ожидать загрузки сразу нескольких ресурсов:

let mapReady = false;
let dataReady = false;

function checkReady() {
    if (mapReady && dataReady) {
        console.log('Все ресурсы готовы');
    }
}

map.whenReady(function () {
    mapReady = true;
    checkReady();
});

fetch('/data.geojson')
    .then(response => response.json())
    .then(data => {
        dataReady = true;
        checkReady();
    });

Подобная схема позволяет синхронизировать:

  • карту;
  • тайловые слои;
  • GeoJSON;
  • изображения;
  • данные внешних API.

Наиболее используемые события загрузки

Событие Объект Назначение
load Map Карта готова к работе
loading TileLayer Начало загрузки тайлов
load TileLayer Завершение загрузки всех тайлов
tileloadstart TileLayer Начало загрузки одного тайла
tileload TileLayer Успешная загрузка тайла
tileerror TileLayer Ошибка загрузки тайла
tileabort TileLayer Отмена загрузки тайла
load ImageOverlay Изображение загружено
error ImageOverlay Ошибка загрузки изображения
load VideoOverlay Видео загружено
error VideoOverlay Ошибка загрузки видео

Система событий загрузки в Leaflet обеспечивает полный контроль над жизненным циклом карты и её ресурсов, позволяя строить надёжные интерфейсы, корректно реагирующие на состояние сетевых запросов, загрузку тайлов и получение пространственных данных.