Яндекс Карты и React Native. Часть 2. HUD и методы карты

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

HUD

Для начала стоит отметить, что я поместил карту (компонент YaMap и все вложенные) и блок с нашим HUD в один контейнер и задал ему ширину и высоту по сто процентов от viewport-а. Это я сделал для того, чтобы удобней было поместить HUD вниз интерфейса с помощью flex-боксов, однако, можно обойтись и без этого. Для того, чтобы отрисовать HUD поверх карты, а не возле справа/слева/вверху/внизу от неё достаточно просто задать в стилях zIndex нужным нам компонент и определить для них position, как absolute. Я сделал HUD, состоящий из трёх кнопок, кнопка-триггер, по ней мы будем переходить на заранее заданные координаты, кнопка зума, а также функцию отдаления (задумка в том, чтобы при нажатии этой кнопки карта перемещалась так, чтобы все маркеры, которые есть на карте помещались на экран). Теперь наш компонент будет выглядеть так:

export default function Habr() {
  
    return (
      <View style={MapStyles.contain}>
  
        <YaMap
        showUserPosition={false}
        rotateGesturesEnabled={false}
        nightMode={true}
        mapType={'vector'}
        initialRegion={{
            lat: 30,
            lon: 30,
            zoom: 7,
            azimuth: 0,
            }}
        style={MapStyles.map}
        >           

            <Marker 
                children={<Image
                    style={MapStyles.marker} 
                    source={{uri: 'ВАШЕ_ИЗОБРАЖЕНИЕ'}} />}
                point={{ lat: 30, lon: 30 }}
                zIndex={6}
            /> 

            <Circle
                center={{lat: 30, lon: 30}}
                radius={60000}
                fillColor='#5789d9'
                strokeColor='#154ca3'
                strokeWidth={4}
                zIndex={5} />

            <Polygon
                points={[
                    { lat: 30.540273, lon: 31.182331 },
                    { lat: 30.070791, lon: 31.928108 },
                    { lat: 29.233658, lon: 31.228942 },
                    { lat: 29.983355, lon: 30.622998 },
                ]}
                fillColor='#5789d9'
                strokeColor='#154ca3'
                strokeWidth={4}
                zIndex={5}
            />

            <Polyline
                points={[
                    { lat: 29.988660, lon: 31.207694 },
                    { lat: 29.364817, lon: 31.176281 },
                    { lat: 28.634771, lon: 30.862143 },
                    { lat: 27.490336, lon: 30.839704},
                ]}
                fillColor='#5789d9'
                strokeColor='#154ca3'
                strokeWidth={4}
                zIndex={4}
            />

        </YaMap>    

        <View style={MapStyles.hud}>

            <TouchableOpacity style={MapStyles.hudButton}>

                <Image 
                    style={MapStyles.hudButtonIMG}
                    source={require('../Assets/iconTarget.png')} />
                
            </TouchableOpacity>

            <TouchableOpacity style={MapStyles.hudButton}>

                <Image 
                    style={MapStyles.hudButtonIMG}
                    source={require('../Assets/iconZoom.png')} />
                
            </TouchableOpacity>

            <TouchableOpacity style={MapStyles.hudButton}>

                <Image 
                    style={MapStyles.hudButtonIMG}
                    source={require('../Assets/iconFull.png')} />
                
            </TouchableOpacity>

        </View>
        
      </View>
    );
}

Стили:

export default MapStyles = StyleSheet.create({

    contain: {

        width: vw(100),
        height: vh(100),
        justifyContent: 'flex-end',

    },

    map: {

        width: vw(100),
        height: vh(100),
        zIndex: 1,

    },

    marker: {

        width: 60,
        height: 60,
        borderRadius: vw(3),
        borderWidth: 4,
        borderColor: 'black',

    },

    hud: {

        position: 'absolute',
        zIndex: 2,
        width: vw(100),
        height: vw(30),
        flexDirection: 'row',
        justifyContent: 'space-evenly',
        alignItems: 'center',

    },

    hudButton: {

        width: vw(20),
        height: vw(20),
        backgroundColor: 'black',
        justifyContent: 'center',
        alignItems: 'center',
        borderRadius: vw(10),

    },

    hudButtonIMG: {

        width: vw(12.5),
        height: vw(12.5),

    },

})

Получаем текущую позицию камеры

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

map = React.createRef();

...

<YaMap
    ref={this.map}
    showUserPosition={false}
    rotateGesturesEnabled={false}
    nightMode={true}
    mapType={'vector'}
    initialRegion={{
        lat: 30,
        lon: 30,
        zoom: 7,
        azimuth: 0,
        }}
    style={MapStyles.map}
>

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

const getCamera = () => {

    return new Promise((resolve, reject) => {

      if (this.map.current) {

        this.map.current.getCameraPosition((position) => {

          resolve(position);

        });

      } else {

        reject('ERROR');

      }
    })

}

Теперь функция getCamera возвращает нам объект с поворотом камеры по азимуту, наклон камеры, координаты на которых находится центр камеры и зум. Будем использовать эту функцию для реализации других функций.

Переход камеры

Теперь реализуем переход камеры на заданную позицию по нажатию кнопки. Для этого пишем асинхронную функцию, которая сначала получит текущую позицию камеры (в данном случае нам нужен зум камеры), а затем сделает переход на нужную нам координату. Будем использовать метод карты setCenter, в который передадим координаты на которые хотим передвинуть центр камеры, текущий зум, поворот по азимуту, наклон, время за которое камера совершит переход и с какой анимацией она это сделает. Для того, чтобы использовать анимацию сначала надо импортировать файл с реализацией анимации в наш проект (есть два вида анимации - smooth и linear):

import { Animation } from 'react-native-yamap';

Теперь реализуем функцию и повесим её на событие нажатия кнопки:

...

const target = async () =>  {

    const camera = await getCamera();
    if(camera != 'ERROR') {

      this.map.current.setCenter(
          { lon: 30, lat: 30 },
          camera.zoom, 
          0, 
          0, 
          0.6, 
          Animation.LINEAR);
                
    }

}

...

<TouchableOpacity
    onPress={target}
    style={MapStyles.hudButton}>

    <Image 
        style={MapStyles.hudButtonIMG}
        source={require('../Assets/iconTarget.png')} />
    
</TouchableOpacity>

Зум камеры

Для того чтобы реализовать функционал кнопки зума будем использовать метод для карты setZoom. Для этого будем получать текущий зум с помощью написанного нами getCamera и будем умножать текущий зум на 1.2 и время за которое выполнится зум выставим на 0.3 секунды.

...

const zoomUp = async () => {

    const camera = await getCamera();
    if(camera != 'ERROR') {

        this.map.current.setZoom(camera.zoom * 1.2, 0.3);
        
    }

}

...

<TouchableOpacity 
    onPress={zoomUp}
    style={MapStyles.hudButton}>

    <Image 
        style={MapStyles.hudButtonIMG}
        source={require('../Assets/iconZoom.png')} />
    
</TouchableOpacity>

Аналогично можно реализовать кнопку уменьшения зума.

Все маркеры на экране

С функционалом последней кнопки заморачиваться сильно не будем, потому что Yandex Maps SDK предоставляет метод fitAllMarkers в который ничего передавать не надо и мы просто повесим его в событие нажатия третьей кнопки.

<TouchableOpacity 
    onPress={() => {

        this.map.current.fitAllMarkers();

    }}
    style={MapStyles.hudButton}>

    <Image 
        style={MapStyles.hudButtonIMG}
        source={require('../Assets/iconFull.png')} />
    
</TouchableOpacity>

Итого

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

Читать предыдущую часть

Источник: https://habr.com/ru/post/713374/


Интересные статьи

Интересные статьи

В данной статье я хочу предоставить переработанную и упорядоченную выжимку документации по настройке "флагов строгости" tsconfig.json. Статья будет полезна как тем, кто т...
Всем привет. Сегодня речь пойдет не совсем о разработке. Я даже не знаю с чего начать. Это просто крик души.Вы когда-нибудь пользовались звонками в телеграм? Это в целом ...
Двухфакторая аутентификация Всё прочитанное вами в первой части касалось идентификации на основании того, что знает запрашивающий. Он знает свой адрес электронной почты, знает, как п...
Начинаем новую неделю с очередной интерпретации официальной документации Flutter в формате «вопрос-ответ». 4-я часть освещает в сравнительном стиле Flutter для веб-разработчиков. Она целиком посв...
Холивар. История рунета. Часть 1. Начало: хиппи из Калифорнии, Носик и лихие 90-е Холивар. История рунета. Часть 2. Контркультура: пАдонки, марихуана и Кремль Холивар. История рунета. Часть 3. ...