Это очень требовательная функция для интеграции карт в ваши веб-приложения. В настоящее время мы покупаем такие вещи, как продукты питания, одежду и всевозможные товары. Обычно мы заинтересованы в отслеживании этих вещей или поиске местоположения определенного места / объекта, который нас интересует. Вот почему многие наши клиенты просят нас интегрировать карты в их приложения.

Ключ API

Конечно, карты Google - это первое, что приходит нам в голову. На данный момент наиболее часто используемые карты в приложениях в мире. Это не очень дорого, и вы получите кредит в 300 долларов за регистрацию своего приложения. Надо сказать, для вашего бизнеса этого хватит надолго. Для использования API карт Google вам необходимо создать свой ключ API и настроить 3 API в облачной консоли Google. Вам необходимо настроить следующие API:

  1. API Адресов
  2. API геокодирования
  3. Maps JavaScript API

Надеемся, что вы успешно получили указанные выше API, следуя инструкциям получить ключ API. Теперь я хочу пролить свет на то, чем будет заниматься наш проект. Основная причина написания этой статьи заключается в том, что я работал над приложением React, и клиент попросил меня создать что-то вроде того, что есть у Airbnb. Мне не удалось найти ни одного хорошего материала, который помог бы мне реализовать эту функциональность. Мне приходится много дней бороться, чтобы реализовать эту функцию, и поэтому я решил поделиться своим путешествием, чтобы другие могли сэкономить свое драгоценное время.

Что мы собираемся делать

Основная задача - разработать карту с информационным окном, которое открывается при нажатии на маркер. Мы собираемся разработать маршруты проезда и создать прокручиваемые списки предметов, как на Airbnb. Всякий раз, когда определенный элемент в списке будет зависать, состояние маркера будет изменено с неактивного на активное, и маркер будет указывать на 2 других места, которые будут показаны на карте. Я собираюсь использовать здесь мою любимую страну Швецию. Популярные города Швеции Стокгольм, Упсала и Вестерос будут представлять 3 маркера. Компонент InfoWindow активируется при щелчке мышью и указывает направления к двум моим любимым городам Фалун и Бурланге, где я когда-то жил.

Выбор библиотеки

Для выполнения этой задачи нам нужно выбрать подходящую библиотеку. Я собираюсь использовать библиотеку response-google-map, так как она имеет наибольшее количество загрузок в неделю и имеет хорошую документацию. Я собираюсь использовать стилизованные компоненты для работы с CSS в этом проекте, поэтому вы должны быть знакомы с его синтаксисом.

Делаем первые шаги, Установка данных и констант

Прежде всего, мы установим react-google-maps в папку нашего проекта с помощью следующей команды:

npm я реагирую-google-карты

Теперь мы создадим фиктивные данные для нашего проекта, следующий код:

Из приведенного выше кода видно, что я передаю 2 массива объектов компоненту карты. Он содержит всю необходимую информацию, такую ​​как заголовок, координаты и src.

Теперь нам нужно создать основное тело компонента карты в папке карты и назвать этот MapContainer. Теперь мы импортируем модули из react-google-map, которые собираемся использовать.

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

Теперь мы собираемся определить некоторые константы, которые будут использоваться для модуля GoogleMap.

Константы решат многие проблемы. Теперь давайте обсудим приведенные выше пары ключей.

  1. scrollwheel: false, - отключает масштабирование карты при прокрутке карты.
  2. mapTypeControl: false, - отключает кнопки карты / спутника, если вам не нужен вид со спутника
  3. fullscreenControl: false, - скрывает кнопку полноэкранного режима по умолчанию на тот случай, если вам нужен более чистый дизайн и эта функция вам не нужна.
  4. streetViewControl: false, - отключает значок человечка, который можно перетащить на карту для включения просмотра улиц.
  5. DEFAULT_CENTER, - объект с координатами широты и долготы по умолчанию, который будет отображаться по умолчанию в случае, если нет данных / данные загружаются и еще не были загружены.
  6. DEFAULT_ZOOM, - целое число
  7. MARKER_SIZE, - целое число, представляющее размер вашего маркера в пикселях
  8. PIXEL_OFFSET.MARKER.X, - Смещение по оси X компонента InfoWindow в пикселях
  9. PIXEL_OFFSET.MARKER.Y, - Смещение по оси Y компонента InfoWindow в пикселях
  10. suppressMarkers: true, - скрыть маркеры по умолчанию
  11. preserveViewport: true, - отключить масштабирование при отображении направления (при вычислении направления карта масштабируется по размеру экрана)

Из приведенного выше фрагмента кода видно, что я передаю два аргумента в MapContainer: источник и пункт назначения. Они представляют собой фиктивные данные, которые я определил выше MOCK_DESTINATIONS_DATA и MOCK_ORIGINS_DATA. Также обязательно передать mapRef в модуль GoogleMap - я создаю его с помощью хука React.useRef. Затем я передаю константы карты как defaultZoom, defaultCenter и defaultOptions.

Чтобы разработать маркеры, мы должны сопоставить модуль Marker и передать результат в качестве дочерних элементов GoogleMap. Сначала мы наносим на карту маркеры, представляющие происхождение - три шведских города, которые я описал выше: Стокгольм, Упсала и Вестерос. Они должны отображаться в виде значков местоположения. Затем мы наносим на карту пункты назначения - мои самые дорогие шведские города: Фалун и Бурланге. Они должны отображаться в виде значков в виде сердечек. Модуль Marker принимает 2 основных параметра: позицию, представляющую координаты вашего маркера (переданные как lat, lng) и значок: здесь мы можем передать объект, где url - это значок (svg, png, url), который мы должны показать для нашей группы маркеры, а scaledSize - размер этих значков. Как мы видим, нам нужно использовать новый window.google.maps.Size, который принимает 2 целых числа (ширину и высоту значка), а затем преобразует их, чтобы объект Icon, используемый в фоновом режиме модуля Marker, мог их правильно обработать.

Изменение собственных правил CSS

Теперь нам нужно создать файл index.js в папке Map и вставить туда следующий код:

Как видно из приведенного выше фрагмента кода, мы импортируем GOOGLE_MAP_URL:

GATSBY_GOOGLE_KEY - это ключ API, который вы получите от Google Maps.

Кроме того, помимо GOOGLE_MAP_URL, мы должны передать карте три элемента div: loadingElement, containerElement и mapElement и сделать их высоту равной 100%. В этом случае мы можем установить высоту нашей карты, используя внешний контейнер, и убедиться, что все три элемента имеют одинаковую высоту - самый обычный случай. Явное указание высоты - важный шаг, поскольку значение по умолчанию - 0, и ваша карта вообще не будет отображаться. Конечно, мы можем установить все 3 размера независимо друг от друга. вы можете задать CSS-стиль для карты - в моем случае я делаю позицию «липкой», вершину до 0 и высоту до 100vh. Теперь мы перепишем классы для компонента MapWrapper.

Вышеуказанное правило удаляет кнопку закрытия из правого верхнего угла InfoWindow.

Приведенный выше код исправит заполнение InfoWindow и позволит правильно растянуть изображение, удалив левое и верхнее смещения, которые повреждают весь макет.

Приведенный выше код помещает собственные кнопки масштабирования в нижний правый угол.

Настройка масштабирования

Когда вы импортируете компонент Map в файл Home.js и используете его там, вы должны увидеть рабочую карту с 5 маркерами:

Как видно из приведенного выше изображения, у нас сразу возникает проблема: масштаб, установленный по умолчанию, довольно мал, а реализованные нами значки выглядят как неприятный беспорядок, поскольку они расположены друг над другом. Мы могли бы изменить значение масштабирования, но в случае, если нам нужно отображать значки во многих странах Европы, более высокое значение масштабирования сделает эти значки невидимыми, и это точно не то, что мы хотим. Вместо этого мы можем сделать масштабирование автоматически в зависимости от положения наших значков. Давайте напишем этот код в компоненте MapContainer перед оператором return:

Ловушка useEffect используется в случае, если параметры назначения и происхождения, передаваемые в MapContainer, изменены. Теперь нам нужно сделать объект границ с помощью new window.google.maps.LatLngBounds (), затем мы перебираем массивы источников и пунктов назначения, в которых хранятся наши данные, и вызовите метод extend, в котором мы должны передать широту и долготу каждого маркера, который хотим показать на карте. В конце мы передаем объект bounds mapRef.current.fitBounds. Это выполнит всю работу, и карта всегда будет увеличиваться таким образом, чтобы все наши маркеры поместились в окне карты.

Добавление InfoWindow, списка карточек и макета

Теперь мы создадим компонент карты, который можно использовать в списке карт, и пользовательский компонент InfoWindow, который будет открываться при нажатии на наши маркеры.

Из приведенного выше кода видно, что компонент InfoWindow очень прост: он получает заголовок и src из данных MOCK_ORIGINS_DATA и показывает изображение с фиксированной высотой и заголовком под ним. Компонент Card имеет точные правила:

Теперь нам нужно создать больше компонентов, которые могут помочь нам расположить карту и список карт рядом, а затем извлечь компонент карты из Home.js и нарисовать этот код вместе со списком карт и вновь созданными компонентами макета в ListMapSection. js файл. Этот код не предназначен для логики Google Maps, но он немного позаботится о том, чтобы макет был организован:

В приведенном выше коде я также добавил перехватчик React.useState - он будет использоваться для хранения идентификатора карты, на которую наведен курсор. Как вы знаете, есть 2 группы маркеров - пункты отправления и пункты назначения, где пункты отправления также представлены в виде карточек в списке карточек. Это означает, что и карты, и маркеры происхождения (они будут отображать 3 шведских города - Стокгольм, Упсала и Вестерос) получают одинаковые идентификаторы из нашего MOCK_ORIGINS_DATA. Теперь нам нужно использовать setHoveredOriginId при наведении курсора на одну из карт и передать ее компоненту карты - эта информация может понадобиться для изменения соответствующего состояния маркера при наведении курсора на карту и, следовательно, указании связи между маркером на карте. и карта в списке карт. Другими словами, мы всегда следим за тем, чтобы при наведении курсора на карту Стокгольма, маркер Стокгольма на карте менял свой цвет, чтобы мы могли мгновенно увидеть, где находится Стокгольм на карте. Для этого мы передаем setHoveredOriginId как onHover компоненту Card, а hoveredOriginId - в Map.

Добавление противодействия, внешнего обработчика кликов и изменение компонентов

Сначала мы должны изменить компонент Card:

Из приведенного выше кода вы можете видеть, что я импортировал модуль use-debounce. Его можно установить, набрав npm i use-debounce в вашем терминале, и вы должны оказаться в папке проекта. Это один из критических моментов, который поможет нам при внедрении Directions API. Теперь сразу же добавь это сюда. Это сделает очень необходимый трюк - он следит за тем, чтобы события onMouseEnter и onMouseLeave не запускались снова и снова, и поэтому, если пользователь по какой-либо причине начнет наводить курсор на карты на очень высокой скорости, эти события не будут запускаться. каждый раз, когда происходит зависание. Это совершенно необходимо для реализации Directions, поскольку каждый раз, когда вы наводите курсор на карточки или щелкаете по маркерам, будет выполняться запрос, и API Google ограничит скорость запросов, чтобы уменьшить рабочую нагрузку, и наше приложение начнет получать ошибки. Об этом совершенно необходимо помнить. Поскольку мы собираемся показывать направления при наведении курсора на карту и выборе маркера, использование useDebouncedCallback имеет важное значение. Если мы не хотим внедрять Directions API в ваш проект, вы можете пропустить этот шаг и использовать состояние без debounced.callback:

Возможно, вы заметили, что обратный вызов onMouseLeave делает значение состояния нулевым. Это действие требуется для того, чтобы убрать выделение маркера и показать направление, если ни одна карта не наведена.

Теперь нам нужно добавить компонент InfoWindow, который имеет следующие функции:

  1. при нажатии на маркеры начала координат он всплывает
  2. если указана конкретная карта из списка карт, она будет удалена
  3. если будет щелчок за его пределами, InfoWindow закроется
  4. при перетаскивании карты он отключит функцию щелчка за пределами, чтобы мы могли настраивать холст карты, не закрывая компонент InfoWindow.
  5. при щелчке, чтобы активировать, он изменит состояние маркера и вернет его в неактивное состояние при наведении карты.

Теперь установите response-outside-click-handler так же, как вы установили все пакеты - в вашем терминале, открытом в папке вашего проекта, напишите npm i response-outside-click-handler. После этого мы должны объявить перехватчик useState для сохранения идентификатора текущего выбранного / выбранного маркера - selectedOriginId. Теперь вы можете установить этот идентификатор с помощью setSelectedOriginId, который вы можете передать как функцию onClick компоненту Marker, как вы можете видеть из приведенного выше кода. Я также определил константу selectedOrHoveredOriginId, которая может хранить либо значение hoveredOriginId, которое является опорой из нашего Списка карт, либо значение, сохраненное в selectedOriginId. После этого selectedOrHoveredOriginId можно использовать в компоненте Mark, чтобы объявить, должен ли значок быть активным или неактивным:

Если мы хотим сделать текущий выбранный маркер неактивным и скрыть InfoWindow, мы можем использовать приведенный ниже перехватчик React.useEffect, который активируется каждый раз, когда существует hoveredOriginId (всякий раз, когда карта зависает), а selectedOriginId имеет значение null.

Чтобы показать компонент InfoWindow, мы должны получить данные, необходимые этому компоненту - широту и долготу:

Используя приведенный выше код, у нас есть объект из источников на основе выбранного идентификатора, и на основе этих данных мы теперь можем показать компонент InfoWindow, который также отправляется в качестве дочерних в GoogleMap:

Вы можете видеть из приведенного выше кода, я даю объект с ключами lat, lng параметру позиции, и я также могу указать вертикальное и горизонтальное смещения компонента InfoWindow относительно маркера - это можно сделать, передав pixelOffset в параметр options с помощью нового window.google.maps.Size, который принимает два числа - смещения X и Y. Затем я передаю свой компонент InfoWindowContent, который будет показывать изображение выбранного города и его заголовок, оборачивая его моим недавно установленным OutsideClickHandler в качестве дочерних элементов. Обработчик принимает 2 параметра - функцию onOutsideClick, в которой я устанавливаю selectedOriginId равным нулю и, следовательно, закрываю InfoWindow и отключаю - логическое значение, чтобы проверить, где мне нужно отключить обработчик OutsideClickHandler. В моем сценарии я использую состояние isClickOutsideDisabled, которое я определил выше. Это setIsClickOutsideDisabled, которое теперь используется в функциях onDragStart и onDragEnd для сравнения между щелчком по карте и перетаскиванием ее холста. Прямо сейчас, когда я перетаскиваю карту, я установил isClickOutsideDisabled в значение true, и поэтому компонент InfoWindow не извлекается OutsideClickHandler - и это именно то поведение, которое нам нужно и к которому мы привыкли.

Добавление сервиса "Маршруты"

Последний шаг этого проекта - добавление службы Directions. Этот API позволяет нам показывать маршруты из одной точки на карте в другую. Вы можете выбрать 4 режима - вождение, ходьба, езда на велосипеде и общественный транспорт. Следует иметь в виду, что полеты не включены в этот список и могут выполняться отдельно с использованием геодезических полилиний. Добавление маршрутов имеет довольно простой API, но может быть проблемой, когда нам нужно отображать несколько направлений одновременно. Как объяснялось выше, при работе с запросами маршрутов у Google есть ограничения на количество запросов одновременно. Другими словами, после тщательного тестирования выясняется, что наиболее подходящий способ запроса множества направлений - это отправка запросов в цикле с таймаутом в 300 миллисекунд. Следовательно, нам нужно убедиться, что пользователь не запрашивает последовательно одни и те же направления - это можно получить с помощью кеширования. И, конечно же, мы должны использовать debounce (это я уже применил в компоненте Card), чтобы пользователь не мог слишком часто инициировать запросы. Если не позаботиться об одном из этих моментов, высока вероятность получения ошибок запроса. Более того, в этих случаях нам придется подождать около 30 секунд, пока вы снова не начнете делать запросы. Если вы хотите узнать больше об ограничениях скорости и ограничениях запросов в этой официальной документации.

Итак, давайте запрограммируем функцию directionRequest:

Извне вызовем и передадим модуль DirectionsService:

Из приведенного выше кода видно, что, помимо DirectionsService, обратный вызов принимает еще 2 параметра - происхождение и пункт назначения. У них есть значения широты и долготы, которые мы должны передать в DirectionsService.route с помощью new window.google.maps.LatLng. И, конечно, мы должны указать режим движения - это можно сделать с помощью константы window.google.maps.TravelMode.DRIVING. В конце мы можем выполнить нашу логику разрешения / отклонения, проверив статус ответа - window.google.maps.DirectionsStatus.OK.

Затем нам нужно создать функцию задержки, которую можно использовать в цикле для каждого запроса маршрута, чтобы убедиться, что они не выполняются слишком часто и мы не получаем ошибок от Google:

Следующее, что нужно сделать - это использовать перехватчик React.useState для разработки состояния для хранения результатов наших маршрутов - это состояние на самом деле представляет собой кэширование данных. Его можно использовать каждый раз, когда пользователь нажимает на маркер или наводит курсор на карточку, чтобы проверить, кэширован ли у нас результат. Если да, запросы не могут быть выполнены, и кешированный результат можно использовать для отображения направлений. состояние имеет значение пустого объекта.

И это основная логика запроса направлений:

В приведенном выше коде я определяю константу directionToSelectedOrHoveredOrigin, которая будет проверять, есть ли у нас кэшированные направления для текущего selectedOrHoveredOriginId. Как я описал выше, я стремлюсь отображать маршруты от маркера отправления до пунктов назначения в этом проекте. В моем сценарии у меня точно есть 2 пункта назначения - Бурленге и Фалун. Поэтому мы должны получить объект selectedOrHoveredOrigin со всеми необходимыми данными и после этого перебрать массив направлений, где я вызываю последний упомянутый directionRequest и передаю в DirectionsService долготу и широту как из моих пунктов назначения, так и из selectedOrHoveredOrigin. После каждого вызова directionRequest я использую свою задержку в 300 мс, передаю результат в tempDirectionsToOrigin, и как только я получаю полные результаты для обоих пунктов назначения, я использую setDirections для кэширования моих недавно полученных маршрутов.

использование модуля DirectionsRenderer из react-google-maps - это последний шаг - мне нужно сопоставить его и передать в качестве дочерних в GoogleMap:

Теперь вы можете раскомментировать последнюю константу - DIRECTIONS_OPTIONS (см. Начало статьи) и передать ее опциям.

Вывод

Получив некоторый опыт работы с React Google Maps, я понял, что на самом деле нам представлен отличный инструмент, но с минимальной предустановленной функциональностью. Большинство задач, которые мы ожидаем решить за нас, придется настраивать вручную.

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

Более того, вам нужно будет решить проблему щелчка вне функциональности, перетаскивания карты, правильного масштабирования и даже установить правильное расстояние между вашим InfoWindow и его маркером. Необходимо изменить и настроить большую часть основного поведения и внешнего вида.

Ситуация усугубляется отсутствием подробной документации, которая поможет вам пройти через весь процесс. Вот почему один из ключевых моментов данной статьи - определить для вас эти биты и показать, как их можно изменить. Еще одно ценное решение связано с внедрением службы направлений, особенно когда она должна работать с несколькими направлениями одновременно.

Конечно, это не те инструменты, которыми мы ограничены - в следующей статье мы построим карту с геодезическими полилиниями для имитации траекторий полета и создания других полезных вещей. Но пока все готово к созданию клона Airbnb.

Больше контента на plainenglish.io