logo
RNNaver Map

Camera Control

Camera control documentation

Overview

This guide explains how to control the Naver Map camera to adjust the map's center point, zoom level, rotation, tilt, and other properties.

Initial vs Controlled Props

NaverMapView provides separate props for initial setup and control:

  • Initial props (initialCamera, initialRegion): Initial values applied only when the map first loads
  • Controlled props (camera, region): Controlled properties that move the map to the specified location whenever the value changes

Controlled Property Usage Pattern

When using controlled properties, implement two-way data binding by combining React state with callbacks:

ControlledCameraExample.tsx
import React, { useState } from 'react';
import { NaverMapView, Camera } from '@mj-studio/react-native-naver-map';

const ControlledCameraExample = () => {
  const [camera, setCamera] = useState<Camera>({
    latitude: 37.5665,
    longitude: 126.9780,
    zoom: 14,
  });

  return (
    <NaverMapView
      style={{ flex: 1 }}
      camera={camera}  // Controlled property: map moves when state changes
      onCameraChanged={({ latitude, longitude, zoom, bearing, tilt }) => {
        // Update state when user manipulates the map
        setCamera({ latitude, longitude, zoom, bearing, tilt });
      }}
    />
  );
};

Performance Optimization Recommendation: Camera callbacks like onCameraChanged are called very frequently when users manipulate the map. For performance reasons, we recommend applying debounce to prevent excessive state updates or API calls.

Usage Scenarios

  • Initial value only (initialCamera/initialRegion): Set the position only for initial map loading, then let users freely manipulate
  • Full control (camera/region): App completely controls map position, moving the map whenever props change

Camera vs Region

Naver Map allows setting map position in two ways:

Camera: Precise zoom level and angle control

CameraExample.tsx
import { NaverMapView, Camera } from '@mj-studio/react-native-naver-map';

const camera: Camera = {
  latitude: 37.5665,
  longitude: 126.9780,
  zoom: 14,
  tilt: 30,     // Tilt (0-60 degrees)
  bearing: 45,  // Rotation (0-360 degrees)
};

<NaverMapView
  style={{ flex: 1 }}
  camera={camera}
  // Or set initial value only
  // initialCamera={camera}
/>

Region: Set by display area range

RegionExample.tsx
import { NaverMapView, Region } from '@mj-studio/react-native-naver-map';

const region: Region = {
  latitude: 37.5665,
  longitude: 126.9780,
  latitudeDelta: 0.05,  // Latitude range
  longitudeDelta: 0.05, // Longitude range
};

<NaverMapView
  style={{ flex: 1 }}
  region={region}
  // Or set initial value only
  // initialRegion={region}
/>

Programmatic Camera Control

Camera Manipulation via Ref

CameraControlExample.tsx
import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import { NaverMapView, NaverMapViewRef, Camera } from '@mj-studio/react-native-naver-map';

const CameraControlExample = () => {
  const mapRef = useRef<NaverMapViewRef>(null);

  const moveToSeoul = () => {
    const seoulCamera: Camera = {
      latitude: 37.5665,
      longitude: 126.9780,
      zoom: 14,
    };

    mapRef.current?.animateCameraTo(seoulCamera);
  };

  const moveToBusan = () => {
    const busanCamera: Camera = {
      latitude: 35.1796,
      longitude: 129.0756,
      zoom: 12,
      tilt: 45,
      bearing: 30,
    };

    // Include animation options
    mapRef.current?.animateCameraTo({
      ...busanCamera,
      duration: 2000,  // Animate for 2 seconds
      easing: 'EaseInOut',
    });
  };

  return (
    <View style={{ flex: 1 }}>
      <NaverMapView
        ref={mapRef}
        style={{ flex: 1 }}
        initialCamera={{
          latitude: 37.5665,
          longitude: 126.9780,
          zoom: 10,
        }}
      />
      <View style={{ position: 'absolute', top: 50, left: 20 }}>
        <Button title="Move to Seoul" onPress={moveToSeoul} />
        <Button title="Move to Busan" onPress={moveToBusan} />
      </View>
    </View>
  );
};

Camera Animation Methods

1. animateCameraTo - Move to Specific Location

AnimateCameraTo.tsx
// Basic usage
mapRef.current?.animateCameraTo({
  latitude: 35.1796,
  longitude: 129.0756,
  zoom: 15,
});

// With animation options
mapRef.current?.animateCameraTo({
  latitude: 35.1796,
  longitude: 129.0756,
  zoom: 15,
  tilt: 30,
  bearing: 45,
  duration: 1500,        // Animation duration (ms)
  easing: 'EaseInOut',   // 'Linear', 'EaseIn', 'EaseOut', 'EaseInOut'
});

2. animateCameraBy - Relative Movement

AnimateCameraBy.tsx
// Relative movement in screen pixel units
mapRef.current?.animateCameraBy({
  x: 100,     // 100 pixels to the right
  y: -50,     // 50 pixels up
  duration: 1000,
  pivot: { x: 0.5, y: 0.5 }, // Rotation center point (0.0-1.0)
});

3. animateRegionTo - Region-based Movement

AnimateRegionTo.tsx
// Display specific region to fit the screen
mapRef.current?.animateRegionTo({
  latitude: 37.5665,
  longitude: 126.9780,
  latitudeDelta: 0.1,
  longitudeDelta: 0.1,
  duration: 2000,
});

4. animateCameraWithTwoCoords - Move to Area Including Two Points

TwoCoordsAnimation.tsx
// Move to optimal camera position that includes both coordinates
mapRef.current?.animateCameraWithTwoCoords({
  coord1: { latitude: 37.5665, longitude: 126.9780 }, // Seoul
  coord2: { latitude: 35.1796, longitude: 129.0756 }, // Busan
  duration: 2000,
  paddingTop: 100,
  paddingBottom: 100,
  paddingLeft: 50,
  paddingRight: 50,
});

Animation Control

Cancel Animation

CancelAnimation.tsx
const handleCancelAnimation = () => {
  mapRef.current?.cancelAnimation();
};

// Usage example: Cancel automatic animation when user directly manipulates map
<NaverMapView
  ref={mapRef}
  onTapMap={() => {
    mapRef.current?.cancelAnimation();
  }}
  // ... other props
/>

Camera State Detection

Camera Change Events

CameraEvents.tsx
<NaverMapView
  onCameraChanged={({
    latitude,
    longitude,
    zoom,
    tilt,
    bearing,
    reason,  // 'Developer' | 'Gesture' | 'Control' | 'Location'
    contentBounds, // Boundaries of area currently displayed on screen
    coveringBounds, // Area covered by map tiles
  }) => {
    console.log('Camera changed:', {
      position: { latitude, longitude },
      zoom,
      tilt,
      bearing,
      reason,
    });

    // Information about area displayed on screen
    console.log('Content bounds:', {
      northEast: contentBounds.northEast,
      southWest: contentBounds.southWest,
    });
  }}

  onCameraIdle={() => {
    console.log('Camera movement finished');
    // Perform necessary tasks after camera movement is complete
  }}
/>

Practical Camera Control Patterns

Move to Area Including All Markers

FitToMarkers.tsx
const fitToMarkers = (markers: Array<{ latitude: number; longitude: number }>) => {
  if (markers.length === 0) return;

  if (markers.length === 1) {
    // If one marker, move to that location
    mapRef.current?.animateCameraTo({
      latitude: markers[0].latitude,
      longitude: markers[0].longitude,
      zoom: 15,
    });
  } else if (markers.length === 2) {
    // If two markers, move to area including both points
    mapRef.current?.animateCameraWithTwoCoords({
      coord1: markers[0],
      coord2: markers[1],
      paddingTop: 100,
      paddingBottom: 100,
      paddingLeft: 100,
      paddingRight: 100,
    });
  } else {
    // If multiple markers, calculate area including all markers
    const latitudes = markers.map(m => m.latitude);
    const longitudes = markers.map(m => m.longitude);

    const minLat = Math.min(...latitudes);
    const maxLat = Math.max(...latitudes);
    const minLng = Math.min(...longitudes);
    const maxLng = Math.max(...longitudes);

    const center = {
      latitude: (minLat + maxLat) / 2,
      longitude: (minLng + maxLng) / 2,
    };

    const delta = {
      latitudeDelta: (maxLat - minLat) * 1.2, // Add extra space
      longitudeDelta: (maxLng - minLng) * 1.2,
    };

    mapRef.current?.animateRegionTo({
      ...center,
      ...delta,
    });
  }
};

Move to Current Location

MoveToCurrentLocation.tsx
import { getCurrentLocation } from '@mj-studio/react-native-naver-map';

const moveToCurrentLocation = async () => {
  try {
    const position = await getCurrentLocation({
      timeout: 10000,
      maximumAge: 60000,
    });

    mapRef.current?.animateCameraTo({
      latitude: position.coords.latitude,
      longitude: position.coords.longitude,
      zoom: 16,
    });
  } catch (error) {
    console.error('Cannot get location information:', error);
  }
};

Coordinate Conversion Utilities

Screen Coordinates ↔ Map Coordinates Conversion

CoordinateConversion.tsx
// Convert screen coordinates to map coordinates
const handleScreenToCoordinate = async () => {
  try {
    const coordinate = await mapRef.current?.screenToCoordinate({
      screenX: 100,
      screenY: 200,
    });
    console.log('Map coordinate:', coordinate);
  } catch (error) {
    console.error('Coordinate conversion failed:', error);
  }
};

// Convert map coordinates to screen coordinates
const handleCoordinateToScreen = async () => {
  try {
    const screenPoint = await mapRef.current?.coordinateToScreen({
      latitude: 37.5665,
      longitude: 126.9780,
    });
    console.log('Screen point:', screenPoint);
  } catch (error) {
    console.error('Coordinate conversion failed:', error);
  }
};

Performance Optimization Tips

Camera Control Performance Optimization:

  1. Optimize animation duration: Too long animations can hurt user experience
  2. Optimize onCameraChanged callbacks: Avoid unnecessary computations and apply debouncing
  3. Prevent animation overlap: Cancel previous animation before starting new one
  4. Appropriate zoom level: Too high zoom levels can affect performance

Camera Change Debouncing

DebouncedCameraChange.tsx
import { useMemo } from 'react';
import { debounce } from 'lodash';

const MapWithDebouncedCamera = () => {
  const debouncedCameraChange = useMemo(
    () => debounce((cameraData) => {
      // Actual logic to process
      console.log('Debounced camera change:', cameraData);
    }, 300),
    []
  );

  return (
    <NaverMapView
      onCameraChanged={debouncedCameraChange}
      // ... other props
    />
  );
};