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:
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
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
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
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
// 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
// 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
// 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
// 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
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
<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
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
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
// 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:
- Optimize animation duration: Too long animations can hurt user experience
- Optimize onCameraChanged callbacks: Avoid unnecessary computations and apply debouncing
- Prevent animation overlap: Cancel previous animation before starting new one
- Appropriate zoom level: Too high zoom levels can affect performance
Camera Change Debouncing
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
/>
);
};