Данная заготовка позволяет создавать на сайте множество карт яндекса, на них размещать метки, добавлять в метки контент, перемещаться к меткам по нажатию кнопок.
Нам понадобится установить в проект библиотеку ymaps
:
npm i ymaps
Затем добавляем в проект js скрипт ymaps.js
:
ymaps.js
import ymaps from 'ymaps';
/**
* @typedef {Object} MapOptions
* @property {number} zoom - Уровень масштабирования карты
* @property {string} ico - URL иконки маркера
* @property {number} iconWidth - Ширина иконки маркера
* @property {number} iconHeight - Высота иконки маркера
* @property {boolean} isScroll - Разрешен ли скролл для карты
* @property {string} centerX - Координата X центра карты
* @property {string} centerY - Координата Y центра карты
*/
/**
* @typedef {Object} MapPoint
* @property {string} x - Координата X точки на карте
* @property {string} y - Координата Y точки на карте
* @property {string} desc - Описание точки
*/
/**
* @callback CallbackFn
* @param {YMap} e - Экземпляр карты
*/
function isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
export default class YMap {
/**
* @param {string} id - Идентификатор элемента карты
* @param {CallbackFn} callbackFn - Функция обратного вызова
*/
constructor(id, callbackFn) {
this._id = id;
this._clFn = callbackFn;
this._$ = document.getElementById(id);
this._options = Object.assign(this.loadOptionsDefault(), { ...this._$.dataset });
this._defaultIcon = this.createIcon();
this._listMarkers = {};
this._markers = this._$ && Array.from(this._$.querySelectorAll('[data-x]'))
.filter((point) => point.dataset.x && point.dataset.y)
.map((point) => ({
desc: point.innerHTML.trim() || '',
x: point.dataset.x || '',
y: point.dataset.y || '',
})) || [];
this.init().then(() => this._markers.forEach((point) => this.addMarker(point)));
}
/**
* Инициализация карты
* @returns {Promise<void>}
*/
async init() {
await ymaps
.load('//api-maps.yandex.ru/2.1/?lang=ru_RU')
.then((maps) => {
this._maps = maps;
this._map = new maps.Map(this._id, {
center: [this._options.centerX, this._options.centerY],
zoom: this._options.zoom,
controls: ['zoomControl', 'fullscreenControl'],
});
})
.catch((error) => console.log('Failed to load Yandex Maps', error));
this._map.behaviors.disable('scrollZoom'); // проверяем, надо ли отрубать скролл
if (isMobileDevice()) {
this._map.behaviors.disable('drag');
}
this._clFn(this);
}
/**
* Загрузка настроек по умолчанию
* @returns {MapOptions} Объект с настройками карты по умолчанию
*/
loadOptionsDefault() {
return {
zoom: 16,
ico: '/assets/img/point-map.svg',
iconWidth: 30,
iconHeight: 30,
isScroll: false,
centerX: '',
centerY: '',
};
}
/**
* Создание иконки маркера
* @param {string} [ico] - URL иконки (если передано)
* @returns {Object} Настройки иконки
*/
createIcon(ico) {
return {
url: ico || this._options.ico,
size: [this._options.iconWidth, this._options.iconHeight],
imgOffset: [-this._options.iconWidth / 2, -this._options.iconHeight],
contentOffset: [0, 0],
};
}
/**
* Добавление маркера на карту
* @param {MapPoint} point - Точка с координатами и описанием
*/
addMarker(point) {
const newMarker = new this._maps.Placemark([point.x, point.y], {
hintContent: '',
balloonContent: point.desc,
}, {
preset: 'islands#dotIcon',
iconColor: '#FF8200',
});
this._listMarkers[`${point.x}-${point.y}`] = newMarker;
this._map.geoObjects.add(newMarker);
}
}
Импортируем в index.js
класс YMap
и добавим карту на нужные элементы. Дополнительно добавим функцию обработчик нажатия на кнопку, чтобы перемещаться к нужным точкам на карте:
main.js
import YMap from './ymap';
/**
* Обработчик кликов по кнопкам, связанным с картой
* @param {YMap} mapObj - Экземпляр карты
*/
function watcherBtn(mapObj) {
const { _id, _map, _maps, _listMarkers, } = mapObj;
const btnList = document.querySelectorAll(`[data-open-map=${_id}]`);
btnList.forEach((btn) => {
btn.addEventListener('click', () => {
document.querySelector('body')?.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
});
_maps.domEvent.manager.group(btn)
.add(['click'], async () => {
const { x = '', y = '', zoom = '' } = btn.dataset;
const marker = _listMarkers[`${x}-${y}`];
const position = [parseFloat(x), parseFloat(y)];
await _map.setCenter(position, zoom || 17, {
checkZoomRange: true,
});
marker?.balloon?.open();
});
});
}
document.addEventListener('DOMContentLoaded', () => {
/** Инициализация карт * */
Array.from(document.querySelectorAll('.map-box')).forEach((map) => {
const id = map.getAttribute('id');
if (!id) return;
new YMap(id, watcherBtn);
});
/** --- Инициализация карт * */
});
Код разметки будет иметь следующий вид:
<div id="map-1" class="map-box" data-center-x="55.751999" data-center-y="37.617734" data-zoom="15">
<div class="d-none" data-x="55.756" data-y="37.620">
<p>Площадь <a href="#">революции</a></p>
</div>
<div class="d-none" data-x="55.751999" data-y="37.617734"></div>
</div>
<div>
<button data-open-map="map-1" data-x="55.756" data-y="37.620" data-zoom="17">Смотреть на карте метку 1</button>
<button data-open-map="map-1" data-x="55.751999" data-y="37.617734" data-zoom="17">Смотреть на карте метку 2</button>
</div>
Если в проекте используется bootstrap, то будет достаточно добавить только стили для класса обертки карты, иначе ещё нужно добавить стиль .d-none
:
.map-box {
width: 400px;
height: 400px;
}
.d-none {
display: none;
}
Готово!