API Яндекс карт
front_hedgehogПривет! Сделала карту, делюсь материалом по API Яндекс карт
🔗 Репозиторий -❗там нет API ключа, нужно подставить свой, без него карта не будет работать
Привет! Про API Яндекс карт.
Задача: сделать карту, в центре которой специальный маркер с логотипом компании. Вокруг должны располагаться маркеры партнерских компаний. Если маркеров в одном месте много для данного масштаба, то они собираются в кластеры. При наведении мыши на маркер партнерской компании появляется tooltip с дополнительной информацией об объекте. Края карты закругленные.
Решение:
1) Получите ключ от Яндекс API. Нужно пройти регистрацию - бесплатно.
Привет! Про API Яндекс карт.
Задача: сделать карту, в центре которой специальный маркер с логотипом компании. Вокруг должны располагаться маркеры партнерских компаний. Если маркеров в одном месте много для данного масштаба, то они собираются в кластеры. При наведении мыши на маркер партнерской компании появляется tooltip с дополнительной информацией об объекте. Края карты закругленные.
Решение:
1️⃣ Получите ключ от Яндекс API. Нужно пройти регистрацию - бесплатно.
2) Подключите API к проекту. Возможно, вам потребуется написать функцию загрузки API. Например:
function loadYandexMapAPI(callback) {
const script = document.createElement('script');
script.src = 'https://api-maps.yandex.ru/2.1/?apikey=<YOUR_API_KEY>&lang=ru_RU';
script.onload = callback;
document.body.appendChild(script);
}
3️⃣ Создайте в HTML-коде блок с id, который вы будете использовать для карты. Например,<div id="map"></div>
*) Если хотите использовать для карты стили, например, закругление углов, то создайте обертку и задавайте стили для неё.
Например:
.map-wrapper {
border: 1px solid transparent;
border-radius: 20px;
overflow: hidden;
outline: none;
transform: translateZ(0);
}
4) Напишите скрипт инициализации карты. Мой скрипт выглядит так:
// создает карту
function initializeMap () {
loadYandexMapAPI(() => {
ymaps.ready(() => {
map = new ymaps.Map(mapElement, {
// центр карты
center: [55.8058, 37.5149],
// масштаб
zoom: 13
});
if (map) {
// добавляет на карту элементы управления масштабом
map.controls.add('zoomControl', {
position: {top: 10, right: 10}
});
// отключает прокрутку карты
map.behaviors.disable('scrollZoom');
// добавляет маркер офиса
addOffice();
// добавляет маркеры партнерских компаний
addPartners();
// активирует маркеры
activateMarkers();
}
});
});
}
// инициализация карты
let map = null;
initializeMap();
Разберем маркер с логотипом:
1. Создаем функцию, которая добавит объект на карту:
function addOffice () {
if (!company.coords || !map) {
return;
}
// создает объект
companyObjectManager = new ymaps.ObjectManager({});
// создает маркер
const companyMarker = generateCompanyMarker(ymaps, company, true, true);
// добавляет маркер в объект
companyObjectManager.add(companyMarker);
// добавляет объект на карту
map.geoObjects.add(companyObjectManager);
}
2. Создаем функцию генерации маркера:
// создает маркер компании
function generateCompanyMarker (ymaps, company, active = false, disableEvents = false ) {
// размер маркера
const sizes = [68, 68];
return {
// географический объект для отображения на карте
type: 'Feature',
id: company.id,
// объект типа "точка" с координатами
geometry: {
type: 'Point',
coordinates: company.coords,
},
properties: {
id: company.id,
name: company.name,
},
options: {
// внешний вид маркера
iconLayout: companyMarkerTemplate(ymaps, company),
// смещение маркера
iconOffset: [-sizes[0] / 2, -sizes[1] / 2],
coordinates: [
[0, 0], sizes
],
// слой отображения маркера
pane: 'overlaps',
// активность маркера
active: active,
// отключение событий
disableEvents: disableEvents,
}
}
}
3. Создаем функцию, которая возвращает html-шаблон для этого маркера:
function companyMarkerTemplate (ymaps, company) {
if (!ymaps) {
return null;
}
const template = ymaps.templateLayoutFactory.createClass(`
<div class="company-marker js-company-marker">
<div class="company-marker__logo">
${company.logo ? company.logo : ''}
</div>
</div>`, {
build: function() {
template.superclass.build.call(this);
},
});
return template;
}
4. Напишем теперь стили для маркера с логотипом:
.company-marker {
display: flex;
justify-content: center;
align-items: center;
width: 48px;
height: 48px;
border-radius: 64px;
background-color: #1C1263;
border: 2px solid #1C1263;
position: relative;
}
.company-marker:after {
content: "";
position: absolute;
bottom: -2px;
left: 50%;
z-index: 1;
border-top: 10px solid #1C1263;
border-right: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 4px solid transparent;
transform: translate(-50%, calc(100% - 1px));
}
API Яндекс карт, продолжение
Нам осталось сделать партнерские маркеры с tooltips. Возможно, мое решение неидеально, и я потом его доработаю, но пока оно такое (было интересно написать без vue. хотя на vue наверно было бы проще, но вряд ли короче)
1. Напишем функцию, которая добавит партнерские объекты на карту
function addPartners () {
if (!map) {
return;
}
partnersObjectManager = new ymaps.ObjectManager({
// группировка в кластеры, если объектов слишком много
// кластер - круг с числом - количеством объектов
clusterize: true,
// минимальное количество объектов для формирования кластера
minClusterSize: 2,
// смещение значка кластера
clusterIconOffset: [0, 0],
// форма и размер значка кластера
clusterIconShape: {
type: 'Circle',
coordinates: [0, 0],
radius: 18,
},
// дополнительный отступ от границы карты при масштабировании
zoomMargin: 48,
// html-шаблон для кластера
clusterIconLayout: clusterMarkerTemplate(ymaps),
// кластер не имеет всплывающего баллона
clusterHasBallon: false,
});
// создание маркеров партнерских компаний
partners.forEach(partner => {
const marker = generatePartnerMarker(ymaps, partner, false);
partnersObjectManager.add(marker);
})
// добавление партнерских маркеров на карту
map.geoObjects.add(partnersObjectManager);
// добавление функционала событий мыши
partnersObjectManager.objects.events
.add(['mouseenter'], e => {
onProjectMarkerMouseEnter(e.get('objectId'));
})
.add(['mouseleave'], e => {
onProjectMarkerMouseLeave(e.get('objectId'));
})
.add('click', e => {
onProjectMarkerMouseClick(e.get('objectId'));
});
}
2. Напишем функцию генерации партнерского маркера
function generatePartnerMarker (ymaps, partner = {}, active=false) {
const sizes = [36, 36];
return {
type: 'Feature',
id: partner.id,
geometry: {
type: 'Point',
coordinates: partner.coords,
},
properties: {
id: partner.id,
name: partner.name,
},
options: {
iconLayout: partnerMarkerTemplate(ymaps, partner),
iconOffset: [-sizes[0] / 2, -sizes[1] / 2],
// прямоугольная область для события маркера
iconShape: {
type: 'Rectangle',
coordinates: [
[0, 0], sizes,
],
},
active: active,
},
}
}
Нам осталось сделать партнерские маркеры с tooltips. Возможно, мое решение неидеально, и я потом его доработаю, но пока оно такое (было интересно написать без vue. хотя на vue наверно было бы проще, но вряд ли короче)
1. Напишем функцию, которая добавит партнерские объекты на карту
function addPartners () {
if (!map) {
return;
}
partnersObjectManager = new ymaps.ObjectManager({
// группировка в кластеры, если объектов слишком много
// кластер - круг с числом - количеством объектов
clusterize: true,
// минимальное количество объектов для формирования кластера
minClusterSize: 2,
// смещение значка кластера
clusterIconOffset: [0, 0],
// форма и размер значка кластера
clusterIconShape: {
type: 'Circle',
coordinates: [0, 0],
radius: 18,
},
// дополнительный отступ от границы карты при масштабировании
zoomMargin: 48,
// html-шаблон для кластера
clusterIconLayout: clusterMarkerTemplate(ymaps),
// кластер не имеет всплывающего баллона
clusterHasBallon: false,
});
// создание маркеров партнерских компаний
partners.forEach(partner => {
const marker = generatePartnerMarker(ymaps, partner, false);
partnersObjectManager.add(marker);
})
// добавление партнерских маркеров на карту
map.geoObjects.add(partnersObjectManager);
// добавление функционала событий мыши
partnersObjectManager.objects.events
.add(['mouseenter'], e => {
onProjectMarkerMouseEnter(e.get('objectId'));
})
.add(['mouseleave'], e => {
onProjectMarkerMouseLeave(e.get('objectId'));
})
.add('click', e => {
onProjectMarkerMouseClick(e.get('objectId'));
});
}
2. Напишем функцию генерации партнерского маркера
function generatePartnerMarker (ymaps, partner = {}, active=false) {
const sizes = [36, 36];
return {
type: 'Feature',
id: partner.id,
geometry: {
type: 'Point',
coordinates: partner.coords,
},
properties: {
id: partner.id,
name: partner.name,
},
options: {
iconLayout: partnerMarkerTemplate(ymaps, partner),
iconOffset: [-sizes[0] / 2, -sizes[1] / 2],
// прямоугольная область для события маркера
iconShape: {
type: 'Rectangle',
coordinates: [
[0, 0], sizes,
],
},
active: active,
},
}
}
3. Напишем функцию активации партнерских маркеров
function activateMarkers () {
if (!map || !partnersObjectManager) {
return;
}
map.events.add('boundschange', function () {
// Обновляем коллекцию маркеров после изменения границ карты, когда все маркеры уже должны быть добавлены на карту
partnerMarkersCollection = document.querySelectorAll('.js-partner-marker');
});
if (partners.length) {
setTimeout(() => {
map.setBounds(map.geoObjects.getBounds(), {
checkZoomRange: true,
zoomMargin: 24,
}).then(() => {
if (map.getZoom() > 13) {
map.setZoom(13);
}
});
}, 0);
} else {
return;
}
}
Что осталось? - У нас нет функций, возвращающих html-шаблоны кластера и партнерского маркера, не хватает функций событий мыши, а также стилей кластера, партнерского маркера и его tooltip.
1. Напишем функцию, возвращающую html-шаблон кластера
function clusterMarkerTemplate (ymaps) {
const template = ymaps.templateLayoutFactory.createClass(
`<div class="cluster-marker">
{{ properties.geoObjects.length }}
</div>`, {
build: function() {
template.superclass.build.call(this);
},
});
return template;
}
2. Напишем функцию, возвращающую html-шаблон партнерского маркера
function partnerMarkerTemplate (ymaps, partner) {
if (!ymaps) {
return;
}
const template = ymaps.templateLayoutFactory.createClass(`
<div class="partner-marker js-partner-marker" id='${partner.id}'>
<div class="partner-marker__pin">
${partner.icon_content ? partner.icon_content : ''}
</div>
<div class="partner-marker__tooltip">
<div class="partner-marker__tooltip-title">
${partner.name ? partner.name : ''}
</div>
<div class="partner-marker__tooltip-text">
${partner.text ? partner.text : ''}
</div>
<div class="partner-marker__tooltip-info">
<span class="partner-marker__tooltip-icon">
${partner.movement_icon_content ? partner.movement_icon_content : ''}
</span>
<span>
${partner.movement_time ? partner.movement_time : ''}
</span>
</div>
</div>
</div>`, {
build: function() {
// Сначала вызовем метод build родительского класса
template.superclass.build.call(this);
// Получаем ссылку на DOM элемент маркера
const element = this.getParentElement().getElementsByClassName('js-partner-marker')[0];
console.log('element', element);
// Теперь у нас есть доступ к элементу, и мы можем применить к нему изменения
const build = () => {
const data = this.getData();
console.log('data', data);
if (!data) {
return;
}
const options = getObjByKeys(data, [
'options',
'_parent',
'_parent',
'_parents',
0,
'_options',
]);
if (!options) {
return;
}
console.log('options', options);
const active = options.active;
active
? element.classList.add('_active')
: element.classList.remove('_active');
};
build();
},
});
return template;
}
3. Дополнительная функция, возвращает значение объекта по найденным ключам
function getObjByKeys(obj, keys) {
return keys.reduce((acc, key) => {
if (acc && Object.prototype.hasOwnProperty.call(acc, key)) {
return acc[key];
} else {
return undefined;
}
}, obj);
}
Осталось доделать события мыши и написать стили
1. События мыши
// наведение мыши на маркер
function onProjectMarkerMouseEnter (id) {
partnersObjectManager.objects.each(o => {
partnersObjectManager.objects.setObjectOptions(o.id, {
active: false,
zIndex: 750,
});
});
partnerMarkersCollection.forEach(marker => {
marker.classList.remove('_active');
});
partnersObjectManager.objects.setObjectOptions(id, {
active: true,
zIndex: 800,
});
console.log(partnerMarkersCollection);
partnerMarkersCollection.forEach(marker => {
if (Number(marker.id) === id) {
marker.classList.add('_active');
}
})
}
// мышь убирают с маркера
function onProjectMarkerMouseLeave (id) {
partnersObjectManager.objects.setObjectOptions(id, {
active: false,
zIndex: 750,
});
partnerMarkersCollection.forEach(marker => {
marker.classList.remove('_active');
});
}
// клик по маркеру - мобильная версия
function onProjectMarkerMouseClick(id) {
const wasActive = id === (activePartner && activePartner.id);
activePartner = wasActive ? {} : partners.find(p => p.id === id);
partners.forEach(partner => {
const isActive = partner.id === (activePartner && activePartner.id);
partnersObjectManager.objects.setObjectOptions(partner.id, {
active: isActive,
});
});
partnerMarkersCollection.forEach(marker => {
if (Number(marker.id) === id) {
marker.classList.toggle('_active');
}
})
}
2. Стили кластера
.cluster-marker {
display: flex;
justify-content: center;
align-items: center;
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px solid #1C1263;
background-color: #fff;
position: relative;
font-weight: bold;
color: #1C1263;
}
3. Стили партнерского маркера и tooltip
.partner-marker {
position: relative;
}
.partner-marker._active .partner-marker__tooltip {
opacity: 1;
z-index: 800;
}
.partner-marker__pin {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px solid #1C1263;
background-color: #fff;
transition: all .3s ease;
cursor: pointer;
position: relative;
}
.partner-marker__pin:after {
content: "";
position: absolute;
bottom: -2px;
left: 50%;
z-index: 1;
border-top: 10px solid #1C1263;
border-right: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 4px solid transparent;
transform: translate(-50%, calc(100% - 1px));
}
/* тоолтип */
.partner-marker__tooltip {
position: absolute;
top: -1rem;
left: 1.8rem;
width: max-content;
max-width: 250px;
padding: 8px 16px;
border-radius: 8px;
display: flex;
flex-direction: column;
background-color: #fff;
opacity: 0;
transform: translate(-50%, -100%);
pointer-events: none;
transition: all .3s ease;
color: #1C1263;
font-family: TT Firs Neue;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 150%;
z-index: 750;
}
.partner-marker__tooltip-title {
margin-bottom: 4px;
font-weight: bolder;
}
.partner-marker__tooltip-text {
margin-bottom: 4px;
font-size: 12px;
}
.partner-marker__tooltip-info {
display: flex;
align-items: center;
}
.partner-marker__tooltip-icon {
margin-right: 8px;
display: flex;
justify-content: center;
align-items: flex-start;
height: 100%;
min-height: 24px;
}
🔗 Репозиторий ❗внимание, без API ключа карта не работает
🔗 Документация: API Яндекс карт
#js