Блог о программировании на PHP, Yii2, 1C-Bitrix

Усовершенствование стандартных Яндекс карт для bitrix

Делаем более красивым отображение стандартных яндекс карт битрикса.

Пункт 1. Стилизация маркера.
Для начала зададим маркер для отображения на картах, который будет использоваться вместо стандартного:

var option = { iconImageHref: '/bitrix/templates/alvik/images/map-marker.png', iconImageSize: [38, 46], iconImageOffset: [-20, -50] };

З.Ы. Пока статикой, потом можно будет засунуть в настройки компонента, ну это уже рефакторинг кода самого компонента.

iconImageHref — путь до картинки маркера, лучше указывать абсолютным,
iconImageSize — размер маркера,
iconImageOffset — смещение центра маркера относительного нижнего хвостика.

Объявим его в функции добавления маркера window.BX_YMapAddPlacemark, и, соответственно добавим его в функцию отображения самого маркера:

var obPlacemark = new ymaps.Placemark(
	[arPlacemark.LAT, arPlacemark.LON],
	properties,
	option
);

Пункт 2. Создание хавера для маркера.

Далее, сделаем еще более красивой нашу карту, создадим эффект хавера на маркере.
После добавления маркера, установим ему евенты:

obPlacemark.events.add('mouseenter', function(e){
	e.get('target').options.set('iconImageHref', placemarkIconsHover.iconImageHref);
});

obPlacemark.events.add('mouseleave', function(e){
	e.get('target').options.set('iconImageHref', placemarkIcons.iconImageHref);
});

Пункт 3. Передача текста в балун маркера.

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

$text = '<div class="title"><a href="'.$arItem["DETAIL_PAGE_URL"].'">'.$arItem["NAME"].'</a></div>';
if( is_array( $arItem["PREVIEW_PICTURE"] ) ){
	$text .= '<div class="img"><img src="'.$arItem["PREVIEW_PICTURE"]["SRC"].'" alt="'.$arItem["NAME"].'" title="'.$arItem["NAME"].'" /></div>';
}

И передаем этот текст в маркер:

$arPlacemarks[] = array(
	"LON" => $coords[1],
	"LAT" => $coords[0],
	"DETAIL_TEXT" => $text,
);

,
И собственно в карту:

...
"MAP_DATA" => serialize(array("yandex_lat" => $city_coords[0],"yandex_lon" => $city_coords[1], "yandex_scale" => 12, "PLACEMARKS" => $arPlacemarks)),
...

Далее, в функции добавления маркера window.BX_YMapAddPlacemark, инициализируем сам текст:

var properties = {};
properties.balloonContent = arPlacemark.DETAIL_TEXT.replace(/\n/g, '<br />');

Пункт 4. Кластеризация.
Добавим чуть-чуть интерактивности для карты. Сделаем объединение близким маркеров в кластеры, чтобы не сливалось при уменьшение масштаба.

Объявляем глобальные настройки для кластера, и, сам кластер:

var clusterIcons = [{ href: '/bitrix/templates/alvik/images/map-cluster.png', size: [50, 50], offset: [-20, -50] }];
var clusterer;

Инициализируем:

var MyIconContentLayout = ymaps.templateLayoutFactory.createClass('<div style="color: red; font-weight: bold;">$[properties.geoObjects.length]</div>');
clusterer = new ymaps.Clusterer({
	clusterIcons: clusterIcons,
	clusterIconContentLayout: MyIconContentLayout,
	zoomMargin: 50,
	showInAlphabeticalOrder: true
});

clusterIcons — изображения для кластера, могут меняться в зависимости от количества входных элементов
clusterIconContentLayout — область для отображения количества входящих элементов
zoomMargin — минимальный отступ от краев карты до маркеров при увеличении масштаба (при клике на кластер)
showInAlphabeticalOrder — сортировка элементов в кластере, по умолчанию сортируется по включению, нужна, если достигнут максимальный зум и несколько элементов входят в кластер.

В функции добавления маркера window.BX_YMapAddPlacemark изменяем добавления маркера на карту, на добавление маркера в кластер

clusterer.add( obPlacemark );

И в функции формирования маркеров function BX_SetPlacemarks_(map) добавляем кластер на карту

map.geoObjects.add( clusterer );

Пункт 5. Добавление хавера на кластер.
И еще раз украсим нашу карту хаверами.

После созданяи кластера пропишем ему евенты:

var clusterIconsHover = [{ href: '/bitrix/templates/alvik/images/map-marker-hover.png', size: [38, 46], offset: [-20, -50] }];
clusterer.events.add('mouseenter', function(e){
	e.get('target').options.set('icons', clusterIconsHover);
});

clusterer.events.add('mouseleave', function(e){
	e.get('target').options.set('icons', clusterIcons);
});

Пункт 6. Подсветка маркера при наведении на ссылку элементы в общем списке.
Интерактивность интерактивность и еще раз интерактивность, допустим у нас выводится общая карта со всеми маркерами элементов и внизу нее выводятся ссылки на эти элементы, хочется, чтобы при наведении на элемент маркер, принадлежащий ему, подсвечивался, для того, чтобы не нужно было прокликивать все маркеры, чтоб узнать к какому элементу он принадлежит.

Для этого добавим скрипт:

	$(document).ready(function(){
		$('.places-list li a').mouseover(function(){
			for( i = 0; i < arObjects.PLACEMARKS.length; i++ ){
				if( $(this).data('id') == arObjects.PLACEMARKS[i].options.get('item') ){
					arObjects.PLACEMARKS[i].events.fire('mouseenter', [arObjects.PLACEMARKS[i].geometry.getCoordinates()]);
				}
			}
		}).mouseout(function(){
			for( i = 0; i < arObjects.PLACEMARKS.length; i++ ){
				if( $(this).data('id') == arObjects.PLACEMARKS[i].options.get('item') ){
					arObjects.PLACEMARKS[i].events.fire('mouseleave', [arObjects.PLACEMARKS[i].geometry.getCoordinates()]);
				}
			}
		})
	})

где переменные arObjects и map_global, были глобально объявлены в компоненте создания карты.

Пункт 7. Добавление стрелок позиционирования маркера при его отсутствии на карте.
Первоначальной идеей было перепрыгивание от маркера к маркеру, при наведении, ну или перепрыгивание на область карты, которую не видно, при наведении на соответствующий маркер. Получилось очень сильно прыгающе, поэтому было решено сделать стрелки для указания, где находится маркер

для этого в обалсти карты создаем стрелки и скрываем их css’ом

<span class="maps-arrow top-arrow"></span>
<span class="maps-arrow bottom-arrow"></span>
<span class="maps-arrow left-arrow"></span>
<span class="maps-arrow right-arrow"></span>

и в событие $(‘.places-list li a’) добавляем условия определения выхождения координат маркера за карту:

	$(document).ready(function(){
		$('.places-list li a').mouseover(function(){
			for( i = 0; i < arObjects.PLACEMARKS.length; i++ ){
				if( $(this).data('id') == arObjects.PLACEMARKS[i].options.get('item') ){
					var pc = arObjects.PLACEMARKS[i].geometry.getCoordinates();
					var mc = map_global.getBounds();

					if( pc[0] < mc[0][0] ){
						$('.bottom-arrow').show();
					}
					if( pc[1] < mc[0][1] ){
						$('.left-arrow').show();
					}
					if( pc[0] > mc[1][0] ){
						$('.top-arrow').show();
					}
					if( pc[1] > mc[1][1] ){
						$('.right-arrow').show();
					}
					if( pc[0] > mc[0][0] && pc[1] > mc[0][1] && pc[0] < mc[1][0] && pc[1] < mc[1][1] ){
						arObjects.PLACEMARKS[i].events.fire('mouseenter', [arObjects.PLACEMARKS[i].geometry.getCoordinates()]);
					}
				}
			}
		}).mouseout(function(){
			for( i = 0; i < arObjects.PLACEMARKS.length; i++ ){
				if( $(this).data('id') == arObjects.PLACEMARKS[i].options.get('item') ){
					var pc = arObjects.PLACEMARKS[i].geometry.getCoordinates();
					var mc = map_global.getBounds();

					if( pc[0] < mc[0][0] || pc[1] < mc[0][1] || pc[0] > mc[1][0] || pc[1] > mc[1][1] ){
						$('.maps-arrow').hide();
					}else{
						arObjects.PLACEMARKS[i].events.fire('mouseleave', [arObjects.PLACEMARKS[i].geometry.getCoordinates()]);
					}
				}
			}
		})
	})

В итоге получилась достаточно интересная и красивая карта :)