Created
May 23, 2019 13:39
-
-
Save newbornfrontender/678946eb30ef0560ece0db8b8831d181 to your computer and use it in GitHub Desktop.
Final map
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="map"> | |
<div class="container map__results"> | |
{% include "blocks/map-results/map-results.html" %} | |
</div> | |
<div id="map" class="map__inner" data-map-center="55.76, 37.64"></div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import $ from 'jquery'; | |
import SimpleBar from 'simplebar'; | |
$(document).ready(() => { | |
const resultsList = document.querySelector('#results-list'); | |
const map = document.querySelector('#map'); | |
// Получаем центр карты | |
const getMapCenter = (target) => { | |
const value = target.dataset.mapCenter; | |
const splitValue = value.split(', '); | |
return [Number(splitValue[0]), Number(splitValue[1])]; | |
}; | |
// Создание кластеризатора с произвольным HTML-макетом иконки кластера | |
class CustomCluster { | |
// Зададаем макет метки кластера | |
createCluster() { | |
return ymaps.templateLayoutFactory.createClass( | |
'<div class="ymap-cluster-icon">{{ properties.geoObjects.length }}</div>', | |
); | |
} | |
// Зададаем активную область иконки кластера | |
createIconShape() { | |
return { | |
type: 'Circle', | |
coordinates: [0, 0], | |
radius: 25, | |
}; | |
} | |
} | |
const init = () => { | |
const geoObjects = []; | |
const customMap = new ymaps.Map( | |
'map', | |
{ | |
center: getMapCenter(map), | |
zoom: 12, | |
controls: [], | |
}, | |
{ | |
searchControlProvider: 'yandex#search', | |
}, | |
); | |
const cluster = new CustomCluster(); | |
const CustomBalloonLayout = ymaps.templateLayoutFactory.createClass( | |
` | |
<div class="ymap-popover"> | |
<a class="ymap-popover-close" href="/"> | |
<span></span> | |
</a> | |
<div class="ymap-popover-arrow"></div> | |
<div class="ymap-popover-inner clinic-card-preview"> | |
$[[options.contentLayout observeSize minWidth=268 maxWidth=268]] | |
</div> | |
</div> | |
`, | |
{ | |
// Строим экземпляр макета на основе шаблона и добавляем его в родительский HTML-элемент | |
build() { | |
this.constructor.superclass.build.call(this); | |
this._$element = $('.ymap-popover', this.getParentElement()); | |
this.applyElementOffset(); | |
this._$element.find('.ymap-popover-close').on('click', $.proxy(this.onCloseClick, this)); | |
}, | |
// Удаляем содержимое макета из DOM | |
clear() { | |
this._$element.find('.ymap-popover-close').off('click'); | |
this.constructor.superclass.clear.call(this); | |
}, | |
// Метод будет вызван системой шаблонов API при изменении размеров вложенного макета | |
onSublayoutSizeChange() { | |
CustomBalloonLayout.superclass.onSublayoutSizeChange.apply(this, arguments); | |
if (!this._isElement(this._$element)) { | |
return; | |
} | |
this.applyElementOffset(); | |
this.events.fire('shapechange'); | |
}, | |
// Сдвигаем балун, чтобы "хвостик" указывал на точку привязки | |
applyElementOffset() { | |
this._$element.css({ | |
left: -(this._$element[0].offsetWidth / 2), | |
// По какой-то причине prettier и stylelint считают строку ниже за CSS | |
// stylelint-disable | |
top: this._$element.find('.ymap-popover-arrow')[0].offsetHeight + 13, | |
}); | |
}, | |
// Закрываем балун при клике на крестик, кидая событие "userclose" на макете | |
onCloseClick(e) { | |
e.preventDefault(); | |
this.events.fire('userclose'); | |
}, | |
// Используется для автопозиционирования (balloonAutoPan) | |
getShape() { | |
if (!this._isElement(this._$element)) { | |
return CustomBalloonLayout.superclass.getShape.call(this); | |
} | |
var position = this._$element.position(); | |
return new ymaps.shape.Rectangle( | |
new ymaps.geometry.pixel.Rectangle([ | |
[position.left, position.top], | |
[ | |
position.left + this._$element[0].offsetWidth, | |
position.top + | |
this._$element[0].offsetHeight + | |
this._$element.find('.ymap-popover-arrow')[0].offsetHeight, | |
], | |
]), | |
); | |
}, | |
_isElement(element) { | |
return element && element[0] && element.find('.ymap-popover-arrow')[0]; | |
}, | |
}, | |
); | |
const CustomBalloonContentLayout = ymaps.templateLayoutFactory.createClass( | |
` | |
<a class="clinic-card-preview__title-link" href="$[properties.balloonTitleLink]"> | |
<h3 class="clinic-card-preview__title">$[properties.balloonTitleText]</h3> | |
</a> | |
<ul class="clinic-card-preview__location"> | |
<li class="clinic-card-preview__location-item">$[properties.balloonMetro]</li> | |
<li class="clinic-card-preview__location-item">$[properties.balloonGeo]</li> | |
</ul> | |
<p class="clinic-card-preview__time">$[properties.balloonTime]</p> | |
<div class="clinic-card-preview__feedback"> | |
$[properties.balloonRating] | |
<p class="clinic-card-preview__score">$[properties.balloonScore]</p> | |
<a class="clinic-card-preview__comments" href="$[properties.balloonCommentsLink]">$[properties.balloonCommentsText]</a> | |
</div> | |
`, | |
); | |
$.ajax({ | |
url: 'data.json', | |
}).done((data) => { | |
data.forEach((item) => { | |
const { | |
id, | |
geometry: { coordinates }, | |
properties, | |
} = item; | |
// Создаем карточки клиник при помощи template тэга и добавляем их в список | |
// --------------------------------------------------------------------- | |
const resultsList = document.querySelector('.map-results__list'); | |
const resultTemplate = document.querySelector('#search-result-card'); | |
// Получаем элемент из template тэга | |
const getNodeFromTemplate = (template, node) => { | |
return template.content.querySelector(node); | |
}; | |
// Добавляем текст | |
const setTextForTemplateNode = (node, text) => { | |
return (getNodeFromTemplate(resultTemplate, node).textContent = text); | |
}; | |
// Добавляем путь ссылки | |
const setHrefForTemplateLink = (node, href) => { | |
return (getNodeFromTemplate(resultTemplate, node).href = href); | |
}; | |
// Очищаем html | |
const trimHtml = (html) => { | |
return html.trim(); | |
}; | |
const templateFeedback = getNodeFromTemplate( | |
resultTemplate, | |
'.clinic-card-preview__feedback', | |
); | |
getNodeFromTemplate(resultTemplate, '.clinic-card-preview').dataset.id = id; | |
setTextForTemplateNode('.clinic-card-preview__title', properties.balloonTitleText); | |
setHrefForTemplateLink('.clinic-card-preview__title-link', properties.balloonTitleLink); | |
setTextForTemplateNode( | |
'.clinic-card-preview__location-item_metro', | |
properties.balloonMetro, | |
); | |
setTextForTemplateNode('.clinic-card-preview__location-item_geo', properties.balloonGeo); | |
setTextForTemplateNode('.clinic-card-preview__time', properties.balloonTime); | |
templateFeedback.innerHTML = trimHtml(` | |
${properties.balloonRating} | |
<p class="clinic-card-preview__score">${properties.balloonScore}</p> | |
<a class="clinic-card-preview__comments" href="${properties.balloonCommentsLink}">${ | |
properties.balloonCommentsText | |
}</a> | |
`); | |
resultsList.appendChild(resultTemplate.content.cloneNode(true)); | |
// Выводим количество клиник на карте | |
// --------------------------------------------------------------------- | |
const clinicsCounter = document.querySelector('.map-results__clinics span'); | |
clinicsCounter.textContent = data.length; | |
// Создаем маркеры, балуны клиник и инициализируем их | |
// --------------------------------------------------------------------- | |
geoObjects[id] = new ymaps.GeoObject( | |
{ | |
geometry: { | |
type: 'Point', | |
coordinates, | |
}, | |
properties, | |
}, | |
{ | |
// Указываем тип макета | |
iconLayout: 'default#imageWithContent', | |
// Добавляем своё изображение иконки метки | |
iconImageHref: 'img/marker.svg', | |
// Указываем размеры метки | |
iconImageSize: [50, 50], | |
// Изменяем положение левого верхнего угла иконки относительно её точки привязки | |
iconImageOffset: [-25, -25], | |
// Не скрывать метку при открытии балуна | |
hideIconOnBalloonOpen: false, | |
balloonShadow: false, | |
balloonLayout: CustomBalloonLayout, | |
balloonContentLayout: CustomBalloonContentLayout, | |
balloonPanelMaxMapArea: 0, | |
}, | |
); | |
}); | |
const clusterer = new ymaps.Clusterer({ | |
gridSize: 32, | |
clusterIconLayout: cluster.createCluster(), | |
clusterIconShape: cluster.createIconShape(), | |
}); | |
clusterer.add(geoObjects); | |
customMap.geoObjects.add(clusterer); | |
// Инициализируем кастомный слайдер после добавления всех карточек | |
// ----------------------------------------------------------------------- | |
new SimpleBar(resultsList, { autoHide: false }); | |
// Перемещаемся к указанному маркеру из списка карточек и открваем балун | |
// ----------------------------------------------------------------------- | |
const resultCards = document.querySelectorAll('.map-results__card'); | |
resultCards.forEach((card) => { | |
const id = card.dataset.id; | |
const { | |
geometry: { _coordinates: coordinates }, | |
} = geoObjects[id]; | |
const moveToPlacemark = () => { | |
customMap.balloon.close(); | |
// Перемещаемся к координатам маркера | |
customMap.panTo(coordinates).then(() => { | |
// Получаем нужный стейт | |
const objectState = clusterer.getObjectState(geoObjects[id]); | |
/** | |
* Проверяем находится ли маркер в кластере | |
* Если находится, тогда увеличиваем масштаб карты до тех пор, пока | |
* не появится нужный маркер. После чего открываем балун маркера | |
*/ | |
if (objectState.isClustered) { | |
// Вызываем менеджер коэффициентов масштабирования | |
customMap.zoomRange.get(coordinates).then((range) => { | |
customMap | |
.setCenter(coordinates, range[1], { | |
// Контролируем доступность масштаба | |
checkZoomRange: true, | |
}) | |
.then(() => { | |
// Карта спозиционировались, после чего открываем балун | |
geoObjects[id].balloon.open(); | |
}); | |
}); | |
} else { | |
geoObjects[id].balloon.open(); | |
} | |
}); | |
}; | |
card.addEventListener('click', () => moveToPlacemark()); | |
card.removeEventListener('click', () => moveToPlacemark()); | |
}); | |
}); | |
}; | |
ymaps.ready(init); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment