В прошлой статье из этого цикла мы устанавливали Яндекс Карты в наш проект и добавляли туда примитивы. В этой статье речь пойдет о взаимодействии с картой и наложение 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, их существует гораздо больше, в том числе ещё функции для работы с камерой. Советую также ознакомиться с первой частью этого цикла. В следующей статье, наверное, покажу, как работать со стилями карты и как её кастомизировать.