Перейти к основному содержимому

Произвольный виджет

Произвольный виджет позволяет встраивать пользовательское содержимое в сцены дашборда через фреймы (<iframe>). Виджет — это HTML-страница, которая подключается к приложению через библиотеку Penpal и получает доступ к API виджета: управлению картой и подписками на события.

В форме создания виджета вы можете указать:

  • URL внешней страницы. Для связи приложения и виджета потребуется подключить Penpal вручную: подробнее см. в разделе Ссылка.
  • HTML- или JavaScript-код. Виджет будет вставлен через атрибут srcdoc в теге <iframe>. Penpal-соединение установится автоматически: подробнее см. в разделе Код.

Создание виджета

Пример создания произвольного виджета
  1. Перейдите на вкладку Дашборды.
  2. Откройте дашборд.
  3. Откройте сцену с помощью стрелок Стрелка влево и Стрелка вправо. Если в дашборде одна сцена, она откроется автоматически.
  4. В верхнем меню дашборда нажмите значок Виджеты.
  5. Выберите тип виджета Произвольный виджет.
  6. Введите URL источника или исходный код для HTML-элемента, который будет отображаться в виджете. Если вы указали URL, подключите Penpal и установите соединение с приложением: подробнее см. в разделе Ссылка.
  7. Нажмите Создать.

Новый виджет автоматически добавится в выбранную сцену дашборда.

Параметры

Общие настройки виджета

Параметр
Описание
Название виджетаНазвание виджета.
Параметр
Описание
Ссылка на ресурсURL-адрес внешней страницы с приложением в формате https://example.com. Для связи виджета и приложения подключите Penpal с помощью кода ниже.

Пример виджета со встроенным сайтом:
Произвольный виджет со ссылкой

Чтобы подключить Penpal, добавьте в код приложения:

<script src="https://pro.2gis.ru/static/penpal/penpal.min.js"></script>
<script>
(function() {
var messenger = new Penpal.WindowMessenger({
remoteWindow: window.parent,
allowedOrigins: ['*']
});
var connection = Penpal.connect({
messenger: messenger,
methods: {
onEvent: function(event) {
window.dispatchEvent(new CustomEvent('pro:widget:event', { detail: event }));
}
},
timeout: 30000
});
window.__proWidgetBridge = {
ready: connection.promise,
destroy: function() { connection.destroy(); }
};
})();
</script>

Код

Параметр
Описание
КодИсходный код на языке HTML- или JavaScript для HTML-элемента, который будет отображаться в виджете. Код выполняется внутри элемента iframe.

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

HTML-код ниже создаёт виджет с элементами управления картой в 2ГИС Про: кнопками увеличения, уменьшения масштаба и перемещения к центру карты (заданной точке). В виджете отображается текущий уровень масштабирования и координаты центра карты.

Пример виджета для управления картой
<!DOCTYPE html>
<html>
<head>
<style>
button { margin: 4px; padding: 8px 16px; }
</style>
</head>
<body>
<button id="zoom-in">+</button>
<button id="zoom-out">-</button>
<button id="center">Центр карты</button>
<p id="info">Загрузка...</p>

<script>
var currentState;

function renderInfo(state) {
document.getElementById('info').textContent =
`Zoom: ${state.zoom}, Center: ${state.center[0].toFixed(2)}, ${state.center[1].toFixed(2)}`;
}

// Автоматическое подключение __proWidgetBridge
__proWidgetBridge.ready.then(async (remote) => {
currentState = await remote.getMapState();
renderInfo(currentState);

document.getElementById('zoom-in').onclick = () =>
remote.setMapState({ zoom: currentState.zoom + 1 }, { animate: true });

document.getElementById('zoom-out').onclick = () =>
remote.setMapState({ zoom: currentState.zoom - 1 }, { animate: true });

document.getElementById('center').onclick = () =>
remote.setMapState(
{ center: [37.615655, 55.768005], zoom: 14 },
{ animate: true, duration: 1000 }
);

// Подписка на изменения карты
await remote.subscribe(['mapState']);
});

window.addEventListener('pro:widget:event', (e) => {
if (e.detail.type === 'mapState') {
currentState = e.detail.payload;
renderInfo(currentState);
}
});
</script>
</body>
</html>

API виджета

Методы ниже доступны через объект remote, полученный из __proWidgetBridge.ready:

const remote = await __proWidgetBridge.ready;

Управление картой

Получение состояния карты

Чтобы получить текущее состояние карты, используйте метод remote.getMapState():

const state = await remote.getMapState();

Пример ответа:

{
center: [37.615655, 55.768005],
zoom: 14,
pitch: 45,
rotation: 90
}

Изменение состояния карты

Чтобы установить состояние карты, используйте метод remote.setMapState(state, options?). Можно передать отдельные параметры.

Параметры состояния карты:

ПолеТипОписание
center[lon, lat]Координаты центра карты: долгота и широта. Допустимые значения долготы: от -180 до 180. Допустимые значения широты: от -90 до 90.
zoomnumberУровень масштабирования. Допустимые значения: от 1 до 22.
pitchnumberНаклон карты. Допустимые значения: от 0 до 60.
rotationnumberПоворот карты. Допустимые значения: от 0 до 360.

Опции анимации:

ПолеТипЗначение по умолчаниюОписание
animatebooleantrueАнимация перехода.
durationnumberДлительность анимации в мс. Допустимые значения: от 0 до 10000.

Пример изменения состояния карты:

// Перемещение карты
await remote.setMapState({ center: [37.615655, 55.768005] });

// Изменение уровня масштабирования и добавление анимации
await remote.setMapState({ zoom: 18 }, { animate: true, duration: 1000 });

// Задание нескольких параметров одновременно
await remote.setMapState({
center: [37.615655, 55.768005],
zoom: 14,
pitch: 45,
rotation: 90,
});

Масштабирование по границам

Чтобы изменить центр и масштаб карты в соответствии с указанными границами, используйте метод remote.fitBounds(params):

await remote.fitBounds({
northEast: [37.62, 55.77],
southWest: [37.61, 55.76],
padding: 20,
maxZoom: 18,
});

Допустимые значения параметра padding: от 0 до 1000.

Получение контекста

Чтобы получить контекст виджета, используйте метод remote.getContext():

const ctx = await remote.getContext();

Пример ответа:

{
widgetId: 'abc123...', // ID виджета
dashboardId: 'def456...', // ID дашборда
sceneId: 'ghi789...', // ID сцены
theme: 'dark', // тема
brand: 'brandName', // бренд
locale: 'en' // язык
}

Подписка на события

Чтобы подписаться на события, используйте метод remote.subscribe(events). События поставляются через CustomEvent в браузере.

Типы событий:

ТипПолезная нагрузкаЧастота срабатывания
mapState{ center, zoom, pitch, rotation }При изменении карты (не чаще 150 мс).
theme{ brand, theme }При смене темы.
language{ locale }При смене языка.

Метод subscribe() создаёт подписку только на будущие обновления. Чтобы получить начальное состояние, запросите его отдельно через getMapState() или getContext().

Чтобы отписаться от событий, используйте remote.unsubscribe(events).

Пример подписки на события:

// Запрос начального состояния
const initialState = await remote.getMapState();

// Подписка на события
await remote.subscribe(['mapState']);

// Обработка событий
window.addEventListener('pro:widget:event', (e) => {
const { type, payload } = e.detail;
switch (type) {
case 'mapState':
console.log('Карта:', payload.center, payload.zoom);
break;
}
});

// Отписка от событий
await remote.unsubscribe(['mapState']);

Ограничения

Ограничения работы с виджетами:

  • Частота вызовов: 30 вызовов в секунду на виджет. При превышении запрос отклоняется.
  • Таймаут соединения: 30 секунд на установку соединения с приложением.
  • Песочница: iframe создаётся с настройкой sandbox="allow-scripts", поэтому отсутствует доступ к cookies, localStorage, sessionStorage и DOM родительской страницы.
  • Валидация параметров: все параметры, переданные через API виджета, проверяются. Невалидные значения отклоняются: см. допустимые значения в разделе API виджета.

Что дальше?