Балун, выходящий за пределы карты

Open in CodeSandbox

Балун можно отображать в пейне, границы которого выходят за пределы карты. Это удобно, когда карта маленького размера, но в балуне нужно отобразить большое количество информации.

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

<!DOCTYPE html>
<html>
    <head>
        <title>Балун, выходящий за пределы карты</title>
        <meta
            http-equiv="Content-Type"
            content="text/html; charset=utf-8"
        />
        <!--
        Укажите свой API-ключ. Тестовый ключ НЕ БУДЕТ работать на других сайтах.
        Получить ключ можно в Кабинете разработчика: https://developer.tech.yandex.ru/keys/
    -->
        <script
            src="https://api-maps.yandex.ru/2.1/?lang=ru_RU&amp;apikey=<ваш API-ключ>"
            type="text/javascript"
        ></script>
        <script src="balloon_out_of_map.js" type="text/javascript"></script>
        <style>
            #map {
                width: 350px;
                height: 250px;
                margin: 60px;
            }
        </style>
    </head>

    <body>
        <div id="map"></div>
    </body>
</html>
var myMap;

ymaps.ready(function () {
    myMap = new ymaps.Map(
        "map",
        {
            zoom: 4,
            center: [54.78, 30.08],
            controls: [],
        },
        {
            searchControlProvider: "yandex#search",
        }
    );
    var myPlacemark = new ymaps.Placemark(
        [55.76, 37.64],
        {
            balloonContent: "Я вышел за границы карты",
        },
        {
            balloonPanelMaxMapArea: 0,
        }
    );
    myMap.geoObjects.add(myPlacemark);

    observeEvents(myMap);

    myPlacemark.balloon.open();
});

function observeEvents(map) {
    var mapEventsGroup;
    map.geoObjects.each(function (geoObject) {
        geoObject.balloon.events
            // При открытии балуна начинаем слушать изменение центра карты.
            .add("open", function (e1) {
                var placemark = e1.get("target");
                // Вызываем функцию в двух случаях:
                mapEventsGroup = map.events
                    .group()
                    // 1) в начале движения (если балун во внешнем контейнере);
                    .add("actiontick", function (e2) {
                        if (
                            placemark.options.get("balloonPane") ==
                            "outerBalloon"
                        ) {
                            setBalloonPane(map, placemark, e2.get("tick"));
                        }
                    })
                    // 2) в конце движения (если балун во внутреннем контейнере).
                    .add("actiontickcomplete", function (e2) {
                        if (
                            placemark.options.get("balloonPane") !=
                            "outerBalloon"
                        ) {
                            setBalloonPane(map, placemark, e2.get("tick"));
                        }
                    });
                // Вызываем функцию сразу после открытия.
                setBalloonPane(map, placemark);
            })
            // При закрытии балуна удаляем слушатели.
            .add("close", function () {
                mapEventsGroup.removeAll();
            });
    });
}

function setBalloonPane(map, placemark, mapData) {
    mapData = mapData || {
        globalPixelCenter: map.getGlobalPixelCenter(),
        zoom: map.getZoom(),
    };

    var mapSize = map.container.getSize(),
        mapBounds = [
            [
                mapData.globalPixelCenter[0] - mapSize[0] / 2,
                mapData.globalPixelCenter[1] - mapSize[1] / 2,
            ],
            [
                mapData.globalPixelCenter[0] + mapSize[0] / 2,
                mapData.globalPixelCenter[1] + mapSize[1] / 2,
            ],
        ],
        balloonPosition = placemark.balloon.getPosition(),
        // Используется при изменении зума.
        zoomFactor = Math.pow(2, mapData.zoom - map.getZoom()),
        // Определяем, попадает ли точка привязки балуна в видимую область карты.
        pointInBounds = ymaps.util.pixelBounds.containsPoint(mapBounds, [
            balloonPosition[0] * zoomFactor,
            balloonPosition[1] * zoomFactor,
        ]),
        isInOutersPane =
            placemark.options.get("balloonPane") == "outerBalloon";

    // Если точка привязки не попадает в видимую область карты, переносим балун во внутренний контейнер
    if (!pointInBounds && isInOutersPane) {
        placemark.options.set({
            balloonPane: "balloon",
            balloonShadowPane: "shadows",
        });
        // и наоборот.
    } else if (pointInBounds && !isInOutersPane) {
        placemark.options.set({
            balloonPane: "outerBalloon",
            balloonShadowPane: "outerBalloon",
        });
    }
}