Yandex Map v3 setPosition относительно попапа
Всем привет. У меня есть кастомный попап при клике на точку карты. Как можно сделать подскролл при клике на точку, что бы по центру был попап?
/** Yandex Map type - Feature to clusterize on a map */
type Feature = GenericPointFeature<LngLat>;
const openPopup = (feature: Feature, e: MouseEvent) => {
mapMarkerActive.htmlElement = e.target as HTMLImageElement
mapMarkerActive.htmlElement.src = '/images/map/marker_active.svg'
map.setLocation({
center: [
Number(feature.properties?.longitude),
Number(feature.properties?.latitude),
] as LngLat,
duration: 500,
})
popup = new YMapMarker(
{
coordinates: feature.geometry.coordinates,
},
popupTemplate(feature)
)
map.addChild(popup)
}
Я вижу это так, что вот тут для latitude нужно вычесть высоту попапа, но как правильно вычесть из координат пиксели, да еще и в зависимости от зума на карте?
map.setLocation({
center: [
Number(feature.properties?.longitude),
Number(feature.properties?.latitude),
] as LngLat,
duration: 500,
})
Нашел в документации вот такие методы https://yandex.com/dev/jsapi-v2-1/doc/en/v2-1/ref/reference/map.Converter Но как будто это немножно не то. Да и для 3.0 версии не нашел эти методы в документации.
Ответы (2 шт):
В общем отчасти спас ChatGPT, но немного было немного нето по координатам. Методом подбора и тестов вывел вот такую функцию
/** Yandex Map type - Feature to clusterize on a map */
type Feature = GenericPointFeature<LngLat>;
const openPopup = (feature: Feature, e: MouseEvent) => {
mapMarkerActive.htmlElement = e.target as HTMLImageElement
mapMarkerActive.htmlElement.src = '/images/map/marker_active.svg'
map.setLocation({
center: [
Number(feature.properties?.longitude),
// 70 это как раз на сколько нужно сместить карту в пикселях относительно маркера/точки
Number(feature.properties?.latitude) + yandexPixelsToLatitude(70, map.zoom),
] as LngLat,
duration: 500,
})
popup = new YMapMarker(
{
coordinates: feature.geometry.coordinates,
},
popupTemplate(feature)
)
map.addChild(popup)
}
function yandexPixelsToLatitude(pixels, zoom) {
// Размер тайла карты, обычно равен 256 пикселей.
const tile_size = 256;
// Масштаб, который изменяется в зависимости от уровня зума.
const scale = 256 * Math.pow(2, zoom);
// Радиус Земли в метрах.
const earth_radius = 6378137;
// Смещение начала координат для преобразования пикселей.
const origin_shift = tile_size / 2.0;
// Количество метров на пиксель, зависящее от масштаба.
// earth_radius * Math.PI * 2 — это длина экватора Земли в метрах.
// tile_size * scale — это количество пикселей на карте при данном масштабе.
const metersPerPixel = (earth_radius * Math.PI * 2) / scale;
// Вычисление координаты 'y' в метрах на основе пиксельного смещения от начала координат.
// (origin_shift - pixels) — это смещение от начала координат в пикселях.
const y = ((origin_shift - pixels) * metersPerPixel) / earth_radius;
// Преобразование 'y' в радианы и вычисление широты.
// Math.exp(y) — это экспоненциальная функция, которая нужна для обратного преобразования из метров в координаты.
// Math.atan() — это арктангенс, который преобразует значение обратно в радианы.
// Умножение на 360 / Math.PI - 90 преобразует радианы в градусы и сдвигает значение, чтобы получить широту.
return ((Math.atan(Math.exp(y)) * 360) / Math.PI - 90)*500;
}
500 это уже мой коэффициент, которые мне дал верный результат.
Если:
- есть точка на карте, в которой установлена метка
- есть popup, центр у которого = центр точки
Можно сделать popup по центу горизонтально через css
transform: translate(-50%, 20px)
(-50% по оси x, на 20px ниже от центра точки).
Теперь popup будет прямо под точкой.
При клике на точку осталось его сделать по центру.
Сделать popup по центру можно примерно как в centerPopup:
// как в `import type { LngLat } from "@yandex/ymaps3-types";`
type LngLat = [lng: number, lat: number];
type Point = {
id: number;
coordinates: LngLat;
};
/** Instance Яндекс Карты */
const map: YMap | null = null;
/**
* Маркер яндекс карты, в котором показан popup.
*
* Центр popup и маркера должны быть в одной точке.
*/
const popupMarker: YMapMarker | null = null;
const centerPopup = () => {
if (!map) {
return;
}
const popup = popupMarker;
if (!popup) {
return;
}
// popup.element - <div class="__ymap-marker">
const popupRectPx = popup.element?.getBoundingClientRect();
/** Центр точки */
const centerCoord: LngLat = popup.coordinates;
/**
* Размер карты в пикселях
*
* @example { x: 657, y: 500 }
*/
const mapSizePx = m.size;
/** bounds - точки границ карты LngLat */
const [mapSizeCoordTopLeft, mapSizeCoordBottomRight] = m.bounds;
/** Сколько longitude (ось x) показано на карте */
const lngX = mapSizeCoordBottomRight[0] - mapSizeCoordTopLeft[0];
/** Сколько latitude (ось y) показано на карте */
const latY = mapSizeCoordTopLeft[1] - mapSizeCoordBottomRight[1];
/** Сколько longitude в одном пикселе */
const lngXPerPixel = lngX / mapSizePx.x;
/** Сколько latitude в одном пикселе */
const latYPerPixel = latY / mapSizePx.y;
/** Центр карты (x) такой же */
const centerLng = centerCoord[0];
/** Половина высоты popup в единицах latitude */
const popupHalfHeightLat = (popupRectPx.height / 2) * latYPerPixel;
/**
* Центр карты (y) = центр точки - 50% popup.
*
* 50% popup будет над новым центром, 50% под.
*/
const centerLat = centerCoord[1] - popupHalfHeightLat;
map.setLocation({
center: [centerLng, centerLat],
duration: 300,
});
};