Клуб API Карт

Разное отображение макета метки в разных масштабах

ivan-zykov
1 октября 2015, 10:20

Добрый день! Подскажите, как мне настроить разное отображение меток в зависимости от масштаба. Метки добавляются через RemoteObjectManager. Затем создаётся свой макет для меток и применяется при добавлении:

var placeLayout = ymaps.templateLayoutFactory.createClass( '<div class="placemark_layout_container">' +

  '<div class="place_layout place_{{properties.iconImageClass}}"></div>' +

  '<div class="title_layout title_layout_place">{{properties.title}}</div>' +

  '</div>' );

remoteObjectManager.objects.events.add('add',function (e) {

  remoteObjectManager.objects.setObjectOptions(e.get('objectId'),{

    iconLayout: placeLayout,

    iconShape: {

      type: 'Rectangle',

      coordinates: [[-9,-30], [9,0]],

    },

  });

});

Пробовал заменять {{properties.title}} на переменную, значение которой менять в зависимости от myMap.getZoom(). Но метки, кажется, кешируются и показывается только изначальный режим отображения.

19 комментариев

Проще и правильнее разместить эту логику в макете, см. метод build

Конкретно то что я вижу, установка опций по событию add не имеет смысла, т.к. Вы можете просто присвоить эти опции через remoteObjectManager.options.set 

Не знаю, что писать в build в моём случае, а пример найти не удаётся.

build: function() {
  placeLayout.superclass.build.call(this);
  // Что тут писать, что слушать?
},

По поводу remoteObjectManager.options.set - спасибо, воспользовался, но применил remoteObjectManager.objects.options.set. Для кластеров, добавляемых через remoteObjectManager, как я понял, аналогичного метода нет?

А что нужно туда писать?

В build из this.getData() можно получить ссылку на карту, оверлей и многое другое.

Можно и на DOM балуна получить ссылку

 

Через remoteObjectManager.options.set нужно использовать префиксы clusterIconLayout, geoObjectIconLayout, или тогда конкретным коллекциям опции добавлять:

remoteObjectManager.objects.options.set('iconLayout', ...)

remoteObjectManager.clusters.options.set('iconLayout', ...)

Спасибо. Путём экспериментов написал рабочий вариант, но скрипт получился не оптимизированным, да и я сомневаюсь, что скрытие подписи через свойства css - удачное решение. Подскажите, где сократить и как иначе можно поступить?

  var placeLayout = ymaps.templateLayoutFactory.createClass(    '' +      '' +      '{{properties.title}}' +    '', {      build: function() {        // Вызываем родительский метод build.        placeLayout.superclass.build.call(this);        var zoom = myMap.getZoom();        var divs = document.querySelectorAll('.title_layout'),i;        if (zoom == 12) {          for (i = 0; i < divs.length; ++i) {            divs[i].style.visibility = "hidden";          }        }        else {          for (i = 0; i < divs.length; ++i) {            divs[i].style.visibility = "visible";          }        }      },      clear: function () {        var zoom = myMap.getZoom();        var divs = document.querySelectorAll('.title_layout'),i;        if (zoom == 12) {          for (i = 0; i < divs.length; ++i) {            divs[i].style.visibility = "hidden";          }        }        else {          for (i = 0; i < divs.length; ++i) {            divs[i].style.visibility = "visible";          }        }        placeLayout.superclass.clear.call(this);      },    }  );

Пробовал засунуть логику в функцию и вызывать в build и clear, но так не работает. И вообще у меня сомнения по поводу применённого мною способа, хоть вроде бы всё и работает.

Кроме масштаба у меня ещё должна быть зависимость от значения в properties у каждой метки. На масштабе 12 я пока просто скрываю подпись через css, но скрывать нужно не у всех меток, а у части в зависимости от значения переменной в properties. Считывать properties получилось через console.log(this.getData().properties.title). Нормальным решением будет заполнять html-контейнер значением параметра?

С уважением, Иван.

Не надо делать querySelectorAll, тем более от документа так вы сейчас в каждой метке ищете и перебираете все метки каждый раз. Надо работать только с DOM текущей метки, т.е не от document, а от parentElement

https://tech.yandex.ru/maps/doc/jsapi/2.1/ref/reference/layout.templateBased.Base-docpage/

 

Нормальным

Сделал как Вы сказали, но теперь при изменении масштаба имена уже добавленных меток не меняются, пока они видны. Нужно переместить карту, чтобы метки выпали из области видимости и вернуться назад - становится всё ок. Посмотрите, пожалуйста, что не так:

   var placeLayout = ymaps.templateLayoutFactory.createClass(    '' +      '' +      '{{properties.title}}' +    '', {      build: function() {        placeLayout.superclass.build.call(this);        var zoom = myMap.getZoom();        if (zoom == 12) {//        this.getParentElement().querySelector('.title_layout').style.visibility = "hidden";          this.getParentElement().querySelector('.title_layout').innerHTML = '';        }        else {//        this.getParentElement().querySelector('.title_layout').style.visibility = "visible";          this.getParentElement().querySelector('.title_layout').innerHTML = this.getData().properties.title;        }      },      clear: function () {        var zoom = myMap.getZoom();        if (zoom == 12) {//        this.getParentElement().querySelector('.title_layout').style.visibility = "hidden";          this.getParentElement().querySelector('.title_layout').innerHTML = '';        }        else {//        this.getParentElement().querySelector('.title_layout').style.visibility = "visible";          this.getParentElement().querySelector('.title_layout').innerHTML = this.getData().properties.title;        }        placeLayout.superclass.clear.call(this);      },    }  );

 

Может, clear() тут и не нужен, а стоит навесить обработчик изменения масштаба? Если это верное решение, то каким образом?

конечно нужен обработчик на boundschange, clear нужен чтобы снимать этот обработчик

У меня возникает ошибка «Uncaught TypeError: Cannot read property 'querySelector' of null». Предполагаю, что я как раз не снимаю обработчик. Подскажите, пожалуйста, как это сделать?

    var placeLayout = ymaps.templateLayoutFactory.createClass(    '' +      '' +      '{{properties.title}}' +    '', {      build: function() {        placeLayout.superclass.build.call(this);        this.mapEventGroup = myMap.events.group();        this.mapEventGroup.add('boundschange', this._renamePlace, this);        this._renamePlace();      },      clear: function () {        placeLayout.superclass.clear.call(this);      },      _renamePlace: function () {        if (myMap.getZoom() == 12) {          var type_place = this.getData().properties.type_place;          if (type_place == "село" || type_place == "город" || type_place == "пгт" || type_place == "посёлок") {            this.getParentElement().querySelector('.title_layout').innerHTML = this.getData().properties.title;          }          else {            this.getParentElement().querySelector('.title_layout').innerHTML = '';          }        }        else {          this.getParentElement().querySelector('.title_layout').innerHTML = this.getData().properties.title;        }      },    }  );

сделай, пожалуйста, минимальный пример на jsfiddle, я так уже что-то плохо соображаю

У меня все работает

            var PlaceLayout = ymaps.templateLayoutFactory.createClass( '' +

              '' +

                '{{properties.title}}' +

              '', {

                build: function () {

                  PlaceLayout.superclass.build.call(this);

                  console.log(this.getParentElement().querySelector);

                }

              });

 

            var objectManager = new ymaps.RemoteObjectManager('http://geohosting.pro/geohosting/api/v1/features/within/tiles?bbox=%t&clusterize=1&zoom=%z', {

                // Опции кластеров задаются с префиксом cluster.

                clusterHasBalloon: false,

                // Опции геообъектов задаются с префиксом geoObject

                // geoObjectOpenBalloonOnClick: false,

                // splitRequests: true,

                 geoObjectIconLayout: PlaceLayout,

 

            });

Добрый день. Так у меня тоже работало. Появилась проблема, когда поставил обработчик на boundschange. Что-то не так с обработчиком. Сделал пример: http://jsfiddle.net/ivanzykov/w5ou03mc/

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

Надо видимо отписываться в clear, некоторые метки из области видимости скрываются и удаляются из контейнера родителя

Обновил код, но как отписываться не знаю. Подскажите, пожалуйста.

Отлично, спасибо, всё работает!

Я верно понимаю, что если надо удалить не все обработчики, а только конкретный, надо писать так:

clear: function () {//  this.mapEventGroup.removeAll();    this.mapEventGroup.remove('boundschange', this._renamePlace, this);    placeLayout.superclass.clear.call(this);},

 

group.removeAll 

удаляет только обработчики назначенные через эту группу

Ещё один вопрос, может, не совсем к месту. А как можно объекты показывать только на определённых масштабах? На других масштабах удалять с карты. При чём интересует решение как для geoQuery, так и для remoteObjectManager. Предполагаю, что надо опять-таки слушать событие boundschange и удалять при необходимости, но, может есть какое коробочное решение или параметр.

Лучше отдельным постом