Задача
В этой статье мы опишем опыт использования Google Maps API для разработки интерфейса сервиса по работе с недвижимостью. И сразу к делу.
Для начала давайте определимся с необходимым функционалом.
Первое, на что стоит обратить внимание — это пользовательский интерфейс (UI). Хорошо, если нам удастся скомбинировать в одном окне:
- карты с маркерами
- фильтры
- результаты фильтрации в виде списка элементов
Результат фильтрации должен быть релевантным, как на карте, так и в списке элементов. Другими словами отфильтрованные элементы должны быть представлены и на карте, и в виде списка.
Второе — это кастомизация. А конкретно: возможность кастомизации элементов на карте и самой карты. Например, нам нужно реализовать маркеры в виде круговых областей со смещенным центром. Это необходимо для того чтобы скрыть реальное расположение недвижимости и показать лишь примерный район.
Третье и самое важное — это UX. Как правило, подобные интерфейсы содержат множество элементов фильтрации (и не только), обеспечивающих обновление информации. Поэтому содержание клиентской части сервиса подвержено частым изменениям. На них влияют различные факторы. Например, изменение состояния фильтров, перемещение по карте, масштабирование карты и т.д.
Таким образом, нам необходимо обеспечить гармоничную реализацию довольно нагруженного интерфейса, кастомизацию, согласно проекту дизайна и управление сложной системой событий. Что же мы делаем? Вооружаемся фреймворком Vue.JS и Google Maps. Как показала практика, выбранный стек технологий позволяет нам реализовать все поставленные задачи.
Реализация
Сразу отметим, что перед началом работы с картой и маркерами, мы получили несколько определенных требований:
- Маркеры — области
- Кластеры маркеров
- При нажатии на маркер открывать информационное окно по недвижимости, прикрепленное к этому маркеру
- При нажатии на кластер открывать информационное окно со слайдером недвижимости в этом кластере
- Набор кластеров и маркеров на карте должен обновляться при каждом ответе сервера с приходом новых данных
Итак, первое, что нам нужно — это инициализировать карту, привязав ее к заранее сверстанному блоку, где она будет развернута.
В нашем случае мы можем реализовать функцию, формирующую и возвращающую объект карты по заданным параметрам:
В приведенном примере функция принимает в качестве аргументов:
- id — идентификатор dom элемента, к которому монтируется карта
- zoom — степень увеличения на карте по умолчанию
- center — координаты центра карты
Полученные данные передаются конструктору карты в виде объекта опций с дополнительными полями. Для управления кластерами мы используем библиотеку Marker Clusterer. Именно в ее конструктор мы передаем полученный объект карты.
Простой пример получения объекта markerCluster выглядит так:
Конструктор принимает:
- map — объект гугл карты
- initialMarkers — массив с начальными маркерами
В итоге мы получаем удобный инструмент для работы с кластерами. С помощью методов markerCluster.addMarkers(markers) и markerCluster.clearMarkers() мы можем управлять набором маркеров и кластеров на карте. markerCluster сам определяет нужно ли создавать кластер в зависимости от текущей степени увеличения и набора маркеров.
Для создания маркера — области мы использовали библиотеку node-MarkerWithLabel. Пример создания маркера:
Теперь нам необходимо кастомизировать класс marker-style с помощью CSS и таким образом получить необходимый маркер в виде условной области.
Для того же, чтобы при клике на маркер выводилась информация по недвижимости, нам необходимо создать объект InfoWindow. Мы используем следующий конструктор:
А обработка события клика по маркеру происходит таким образом:
Теперь при клике на маркер мы устанавливаем контент объекта infoWindow, передавая ему сформированный DOM элемент с информацией о собственности и отображаем окно с помощью метода infoWindow.open(). Далее объект карты и маркер передаются в качестве аргументов.
При клике на кластер макреров в объекте infoWindow нам нужно сформировать слайдер. Для этого, в первую очередь, нужно добавить обработчик клика по кластеру с помощью такого выражения:
В обработчике handler, с помощью метода markerClusterer.getMarkers(), мы можем получить информацию о содержащихся в кластере маркерах. А на ее основе построить DOM элемент со слайдером, который и передадим объекту infoWindow. По аналогии с тем, как это было реализовано для одиночного маркера.
Таким образом, получая данные с сервера, мы создаем массив маркеров, добавляя им обработчик клика, и передаем этот массив в метод markerCluster.addMarkers(). В этом нам помогает библиотека MarkerWithLabel.
Объект markerCluster добавляет маркеры на карту и, при необходимости, создает кластеры маркеров. При клике на них отображается список элементов недвижимости в виде слайдера.
Используя вышеупомянутые примеры, мы можем реализовать конструктор интерфейса, отвечающий за работу карты в целом. А еще мы можем запросто интегрировать его с Vue.js.
Демонстрируем. Существует vue метод mounted. Он вызывается по окончании инициализации vue компонента. Именно в него мы добавляем подобный код:
Таким образом, как карта, так и построенный на ее основе объект, реализующий интерфейс для управления кластерами, будут доступны из любого vue метода.
Поехали дальше. Внутри объекта markerCluster мы реализовали метод addMarkersToCluster, которому мы передаем результат обращения к серверу. В нем же формируется массив маркеров и передается непосредственно объекту кластера. А используя объект карты map, мы можем определить:
- текущий центр карты (this.map.getCenter())
- степень увеличения (this.map.getZoom())
- координаты правого верхнего и левого нижнего углов карты (this.map.getBounds()) — они нужны для передачи на сервер текущего поля видимости при запросе данных
Передавая идентичные данные кластеру и vue методу, отвечающему за отображение актуальных элементов собственности, мы получаем синхронизацию объектов в списке и элементов на карте. Примеры интерфейса:
Вывод
Что мы получили в итоге?
- Удобный инструмент для управления контентом на карте. Он реально упрощает работу с подобным типом интерфейсов.
- Гибкость и удобство разработки. Здесь нам помог фреймворк Vue.js, обеспечивающий скорость и эффективность реализации высоконагруженных интерфейсов.
- Отличный UX. Например, если пользователь хочет поделиться ссылкой на текущую выборку, вместе с обычными данными мы передаем параметры карты. И даже разворачиваем интерфейс вместе с теми параметрами карты, которыми хотел поделится пользователь.
Как видите, результат оправдал даже довольно оптимистичные ожидания. Если у вас остались вопросы, свяжитесь с нами напрямую.