You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native Maps中标记无法连续移动的问题求助

Fixing Jumpy Marker Movement in React Native Maps

Got it, let's tackle that choppy marker movement issue! The main problem with your current implementation is twofold: location updates might be coming in with large gaps (causing jumps), and the animation handling isn't set up to smoothly interpolate between consecutive positions. Here's how to fix it step by step:

Key Issues in Your Current Code

  • Geolocation Configuration: The maximumAge: 1000 allows using cached location data up to 1 second old, which can lead to delayed or jumpy updates.
  • Animation Handling: On iOS, coordinate.timing() can get interrupted by new location updates, and on Android, animateMarkerToCoordinate has limited smoothness.
  • No Interpolation: You're directly jumping to new coordinates instead of animating the marker through intermediate points.

Solution: Smooth Animated Marker with Optimized Location Updates

Step 1: Optimize Geolocation Settings

Tweak the watchPosition options to prioritize fresh, precise updates and only trigger when the user actually moves:

  • Set maximumAge: 0 to avoid cached locations
  • Add distanceFilter: 1 to only update when the user moves at least 1 meter
  • Keep enableHighAccuracy: true for the most precise data

Step 2: Use Animated.ValueXY for Smooth Cross-Platform Animation

Replace AnimatedRegion with Animated.ValueXY to have full control over the marker's position, and use spring animations (which feel more natural for movement) instead of basic timing.

Step 3: Update Path and Distance Only When Meaningful

Avoid adding every single location update to your route coordinates (which can clutter the path) – only add points when the distance from the last point is significant (e.g., >1 meter).

Modified Code Implementation

import React from 'react';
import { View, TouchableOpacity, Text, Platform, Animated } from 'react-native';
import MapView, { Polyline, Marker } from 'react-native-maps';
import haversine from 'haversine';

const LATITUDE = 37.78825; // Replace with your initial latitude
const LONGITUDE = -122.4324; // Replace with your initial longitude
const LATITUDE_DELTA = 0.005;
const LONGITUDE_DELTA = 0.005;

class AnimatedMarkers extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      routeCoordinates: [],
      distanceTravelled: 0,
      prevLatLng: { latitude: LATITUDE, longitude: LONGITUDE },
      currentRegion: {
        latitude: LATITUDE,
        longitude: LONGITUDE,
        latitudeDelta: LATITUDE_DELTA,
        longitudeDelta: LONGITUDE_DELTA,
      },
    };
    // Use Animated.ValueXY for smooth coordinate control
    this.animatedCoordinate = new Animated.ValueXY({
      x: LONGITUDE,
      y: LATITUDE,
    });
  }

  componentWillMount() {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        // Set initial animated position
        this.animatedCoordinate.setValue({ x: longitude, y: latitude });
        this.setState({
          prevLatLng: { latitude, longitude },
          currentRegion: {
            latitude,
            longitude,
            latitudeDelta: LATITUDE_DELTA,
            longitudeDelta: LONGITUDE_DELTA,
          },
        });
      },
      (error) => alert(error.message),
      { enableHighAccuracy: true, timeout: 20000, maximumAge: 0 }
    );
  }

  componentDidMount() {
    this.watchID = navigator.geolocation.watchPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        const newCoordinate = { latitude, longitude };
        const distance = haversine(this.state.prevLatLng, newCoordinate);

        // Animate marker to new position with spring (smooth, natural movement)
        Animated.spring(this.animatedCoordinate, {
          toValue: { x: longitude, y: latitude },
          useNativeDriver: false, // Required since we're animating map coordinates
          stiffness: 100, // Adjust for faster/slower animation
          damping: 15, // Adjust for bounce effect
        }).start();

        // Update path and distance only if movement is significant (>1m)
        if (distance > 0.001) { // 0.001 km = 1 meter
          this.setState((prevState) => ({
            routeCoordinates: [...prevState.routeCoordinates, newCoordinate],
            distanceTravelled: prevState.distanceTravelled + distance,
            prevLatLng: newCoordinate,
            currentRegion: {
              latitude,
              longitude,
              latitudeDelta: LATITUDE_DELTA,
              longitudeDelta: LONGITUDE_DELTA,
            },
          }));
        }
      },
      (error) => console.log(error),
      {
        enableHighAccuracy: true,
        timeout: 20000,
        maximumAge: 0,
        distanceFilter: 1, // Only update if user moves 1m+
      }
    );
  }

  componentWillUnmount() {
    navigator.geolocation.clearWatch(this.watchID);
  }

  render() {
    return (
      <View style={{ flex: 1 }}>
        <MapView
          style={{ flex: 1 }}
          showUserLocation
          followUserLocation
          loadingEnabled
          region={this.state.currentRegion}
        >
          <Polyline
            coordinates={this.state.routeCoordinates}
            strokeWidth={5}
            strokeColor="#1a73e8"
          />
          <Marker.Animated
            coordinate={{
              latitude: this.animatedCoordinate.y,
              longitude: this.animatedCoordinate.x,
            }}
          />
        </MapView>
        <View style={{ position: 'absolute', bottom: 20, left: 20, right: 20 }}>
          <TouchableOpacity style={{ backgroundColor: 'white', padding: 15, borderRadius: 10 }}>
            <Text style={{ fontSize: 16, fontWeight: '500' }}>
              {parseFloat(this.state.distanceTravelled).toFixed(2)} km
            </Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

export default AnimatedMarkers;

Key Improvements Explained

  • Animated.ValueXY: Gives fine-grained control over the marker's position, allowing smooth spring animations that transition naturally even when new location updates come in.
  • Spring Animation: The stiffness and damping parameters let you adjust the marker's movement feel – lower stiffness makes it slower, higher damping reduces bounce.
  • Optimized Location Updates: distanceFilter:1 ensures we only process meaningful movement, reducing unnecessary animations and cluttered path points.
  • Safe State Management: Using functional setState ensures we access previous state correctly, avoiding race conditions.

Additional Tips

  • Test on a real device: Simulator location updates can be choppy or unrealistic.
  • Adjust spring parameters: Tweak stiffness and damping to match your app's desired feel (e.g., higher stiffness for snappier movement).
  • Handle location errors gracefully: Add fallback logic if high accuracy isn't available (e.g., switch to low accuracy mode).

内容的提问来源于stack exchange,提问作者vikrantnegi

火山引擎 最新活动