如何在React Native Map中进行缩放?

43

我正在使用react-native构建地图应用程序。 我使用的API来自此链接:https://github.com/lelandrichardson/react-native-maps

下面是我在应用程序中加载地图的代码。我想知道如何在地图上设置缩放值,以及如何在用户单击地图上的按钮时更改缩放值。

我应该使用哪个缩放API来实现这一点?

import React, { Component, StyleSheet, View, TextInput } from "react-native";
import MapView from "react-native-maps";

class MapPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      region: {
        latitude: 4.21048,
        longitude: 101.97577,
        latitudeDelta: 10,
        longitudeDelta: 5,
      },
    };
  }

  render() {
    return (
      <View style={styles.container}>
        <TextInput style={styles.inputText}>Map</TextInput>
        <MapView
          style={styles.map}
          mapType={"standard"}
          region={this.state.region}
          zoomEnabled={true}
          scrollEnabled={true}
          showsScale={true}
        />
      </View>
    );
  }
}

module.exports = MapPage;

const styles = StyleSheet.create({
  map: {
    position: "absolute",
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  container: {
    flexDirection: "row",
    justifyContent: "space-between",
    padding: 30,
    flex: 1,
    alignItems: "center",
  },
  inputText: {
    height: 36,
    padding: 4,
    marginRight: 5,
    flex: 4,
    fontSize: 18,
    borderWidth: 1,
    borderColor: "#48BBEC",
    borderRadius: 8,
    color: "#48BBEC",
  },
});

7个回答

123

您应该使用animateToRegion方法(见这里)。

它接受一个包含latitudeDeltalongitudeDelta的区域对象。使用它们来设置缩放级别。

更新:

Region对象中,latitudelongitude指定中心位置,latitudeDeltalongitudeDelta指定可视地图区域的跨度。

这张图片来自这篇博客文章很好地说明了它(LatΔ 和 LngΔ)。enter image description here


1
纬度和纬度增量之间有什么区别?是否有文档来解释它们? - Joey Yi Zhao
谢谢您的回复。有没有示例可以说明如何使用animateToRegion? - Joey Yi Zhao
3
好的,这是一个例子:https://github.com/lelandrichardson/react-native-maps/blob/master/example/examples/DisplayLatLng.js#L41 - David
1
那么,如何使用自定义的放大和缩小按钮设置增量? - Alexander Pravdin

5
新的React Native Maps API 提供了使用 zoom 参数调用 animateCamera 方法的选项。
const MapComponent= (props: any) => {

const map: LegacyRef<MapView> = useRef(null);

const onZoomInPress = () => {
    map?.current?.getCamera().then((cam: Camera) => {
        cam.zoom += 1;
        map?.current?.animateCamera(cam);
    });
};

return (
        <View>
            <MapView
                ref={map}
                provider={PROVIDER_GOOGLE}
                region={region}>
            </MapView>
            <ButtonComponent
                style={{position: 'absolute', bottom: 400, left: 0}}
                onPress={onZoomInPress}>
                Zoom In
            </MainButtonBlue>
        </View>
    );
}

1
不适用于IOS。 - Jarek Bielicki

3

我能够使用Dimensions.get('window');使这个工作。

            const window = Dimensions.get('window');
            const { width, height }  = window
            LONGITUDE_DELTA = LATITUD_DELTA + (width / height)

默认情况下,LATITUD_DELTA = 0.0922。 然后,只需使用 <MapView> 中的 prop onRegionChangeComplete 更新这些值。


为什么它会给你不同的值?它指的是屏幕大小,无论地图缩放级别如何都保持不变。 - Shanaka
10
我认为应该将其翻译为:经度差值应为纬度差值乘以宽高比。具体表述为:LONGITUDE_DELTA = LATITUDE_DELTA * (width / height)。 - Shanaka
@Shanaka 你为什么这么说? - Cristian Canales
这种方法在此示例中使用:https://github.com/react-native-community/react-native-maps/blob/master/example/examples/DisplayLatLng.js#L12 - Anna Ira Hurnaus
6
我们如何得出 LATITUDE_DELTA = 0.0922?这意味着什么?我们该如何计算它? - Madhusudhan

1

这是我做的事情,对我非常有效:

function getRegion(origin, destination, zoom) {
  const oLat = Math.abs(origin.latitude);
  const oLng = Math.abs(origin.longitude);
  const dLat = Math.abs(destination.latitude);
  const dLng = Math.abs(destination.longitude);

  return {
      latitude: (origin.latitude + destination.latitude) / 2,
      longitude: (origin.longitude + destination.longitude) / 2,
      latitudeDelta: Math.abs(oLat - dLat) + zoom,
      longitudeDelta: Math.abs(oLng - dLng) + zoom,
  };                                                                                  
}

谢谢,这与在React Native和Expo中使用MapView上的useRef结合使用,对我很有帮助。我将此函数在大约1000ms的setTimeout中使用,以便地图加载完成,然后使用您的getRegion实现正确的缩放级别动画效果。 - Jean Roux

1
  const handleZoomIn = () => {
    map.current?.getCamera().then((cam: Camera) => {
      if (Platform.OS === 'android') {
        cam.zoom += 1;
      } else {
        cam.altitude /= 2;
      }
      map.current?.animateCamera(cam);
    });
  };

  const handleZoomOut = () => {
    map.current?.getCamera().then((cam: Camera) => {
      if (Platform.OS === 'android') {
        cam.zoom -= 1;
      } else {
        cam.altitude *= 2;
      }
      map.current?.animateCamera(cam);
    });
  };

欢迎来到本站。花点时间解释一下你的代码,而不是直接倾倒代码。 - DᴀʀᴛʜVᴀᴅᴇʀ
你好,代码应该放在哪里?请添加一些文本。 如何给出好的答案 - Martin54
这是针对功能组件的。调用这两个函数来为您创建的自定义缩放按钮实现缩小和放大功能。 - Sayan Dey
<TouchableOpacity style={{ position: 'absolute', bottom: 150, left: 10, backgroundColor: '#1f38d4', padding: 10, borderRadius: 8, elevation: 10, }} onPress={() => { handleZoomIn() }}> <FontAwesome5 name="plus" size={20} color="#fff" /> </TouchableOpacity> - Sayan Dey

0

我基于https://github.com/tuupola/php_google_maps中的Mercator数学原理创建了以下内容

关键函数是mercatorDegreeDeltas(latitude, longitude, width, height, zoom),它会返回指定纬度/经度中心点、地图尺寸和缩放级别(1-20)的{ latitudeDelta, longitudeDelta }

import React from 'react';
import { useWindowDimensions } from 'react-native';
import MapView from 'react-native-maps';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useHeaderHeight } from '@react-navigation/elements';

const MERCATOR_OFFSET = Math.pow(2, 28);
const MERCATOR_RADIUS = MERCATOR_OFFSET / Math.PI;

function mercatorLatitudeToY(latitude) {
  return Math.round(
    MERCATOR_OFFSET - 
    (
      (
        MERCATOR_RADIUS *
          Math.log(
            (1 + Math.sin(latitude * (Math.PI / 180))) /
            (1 - Math.sin(latitude * (Math.PI / 180)))
          )
      ) / 2
    )
  );
}

function mercatorLongitudeToX(longitude) {
  return Math.round(
    MERCATOR_OFFSET +
      (
        (
          (MERCATOR_RADIUS * longitude) * Math.PI
        ) / 180
      )
  );
}

function mercatorXToLongitude(x) {
  return (
    (
      (x - MERCATOR_OFFSET) / MERCATOR_RADIUS
    ) * 180
  ) / Math.PI;
}

function mercatorYToLatitude(y) {
  return (
    (
      (
        Math.PI / 2
      ) -
        (2 * Math.atan(
              Math.exp(
                (
                  y - MERCATOR_OFFSET
                ) / MERCATOR_RADIUS
              )
            )
        )
    ) * 180
  ) / Math.PI;
}

function mercatorAdjustLatitudeByOffsetAndZoom(latitude, offset, zoom) {
  return mercatorYToLatitude(mercatorLatitudeToY(latitude) + (offset << (21 - zoom)));
}

function mercatorAdjustLongitudeByOffsetAndZoom(longitude, offset, zoom) {
  return mercatorXToLongitude(mercatorLongitudeToX(longitude) + (offset << (21 - zoom)));
}

function mercatorDegreeDeltas(latitude, longitude, width, height, zoom) {

  if (!zoom) {
    zoom = 20;
  }

  const deltaX = width / 2;
  const deltaY = height / 2;

  const northLatitude = mercatorAdjustLatitudeByOffsetAndZoom(latitude, deltaY * -1, zoom);
  const westLongitude = mercatorAdjustLongitudeByOffsetAndZoom(longitude, deltaX * -1, zoom);
  const southLatitude = mercatorAdjustLatitudeByOffsetAndZoom(latitude, deltaY, zoom);
  const eastLongitude = mercatorAdjustLongitudeByOffsetAndZoom(longitude, deltaY, zoom);

  const latitudeDelta = Math.abs(northLatitude - southLatitude);
  const longitudeDelta = Math.abs(eastLongitude - westLongitude);

  return { latitudeDelta, longitudeDelta };
}

// Somewhat arbitrarily, Riverside Park, Independence, KS 67301
const CENTER_UNITED_STATES = {
  latitude: 37.24435373025407,
  longitude: -95.70234410503208,
};

export default function MapViewWrapper() {
  const { width, height } = useWindowDimensions();
  const tabBarHeight = useBottomTabBarHeight();
  const headerHeight = useHeaderHeight();
  const initialRegion = React.useRef(null);
  const availableHeight = height - tabBarHeight - headerHeight;

  // Only calculate initial region once
  if (!initialRegion.current) {
    const { latitudeDelta, longitudeDelta } = mercatorDegreeDeltas(
      CENTER_UNITED_STATES.latitude,
      CENTER_UNITED_STATES.longitude,
      width,
      availableHeight,
      4,
    );
    
    initialRegion.current = {
      latitude: CENTER_UNITED_STATES.latitude,
      longitude: CENTER_UNITED_STATES.longitude,
      latitudeDelta: latitudeDelta,
      longitudeDelta: longitudeDelta,
    };
  }

  return (
    <MapView
      initialRegion={initialRegion.current}
      style={{ width: width, height: availableHeight }}
    />
  );
}

至少存在一个问题:如果你将缩放从4更改为3,它不会正确居中,但更大的缩放值可以正常工作。我现在不需要较低的缩放值,因此我没有进一步研究过数学(也许是某种溢出?)。


-1
//Example of Pinch to Zoom Image in React Native
//https://aboutreact.com/react-native-pinch-to-zoom-image/

//import React in our code
import React from 'react';

//import all the components we are going to use
import { SafeAreaView, StyleSheet, View } from 'react-native';

//import ImageViewer which will help us to zoom Image
import ImageViewer from 'react-native-image-zoom-viewer';

const App = () => {
  const images = [
    {
      url:
        'https://raw.githubusercontent.com/AboutReact/sampleresource/master/sample_img.png',
    },
    {
      url:
        'https://raw.githubusercontent.com/AboutReact/sampleresource/master/old_logo.png',
    },
  ];

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <View style={styles.container}>
        <ImageViewer imageUrls={images} renderIndicator={() => null} />
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#F5FCFF',
    flex: 1,
  },
});

export default App;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接