Resium(Cesium-React)实现相机跟踪目标时保持自定义缩放高度
问题描述
我有一个基于Resium的React组件,相机可以根据数据动态移动。当前代码能跟踪目标点,但每次更新时都会重置相机高度。我想实现跟踪目标点的同时允许自由缩放,且不重置相机高度,请问该怎么修改?
原代码如下:
const imgProvider = new OpenStreetMapImageryProvider({ url : 'https://a.tile.openstreetmap.org/' }); function TrackingMap(props) { const [data, setData] = React.useState({longitude: 4.3571, latitude: 52.0116}); const startCoordinates = Cartesian3.fromDegrees(data.longitude, data.latitude, 10000); const pointCoordinates = Cartesian3.fromDegrees(data.longitude, data.latitude, 0); function move() { setData((old) => { return {longitude: old.longitude+0.00025, latitude: old.latitude}; }) } setTimeout(move, 200); return <div> <Viewer full resolutionScale={0.5} baseLayerPicker={true} fullscreenButton={false} homeButton={false} sceneModePicker={true} selectionIndicator={true} timeline={false} animation={false} geocoder={false} imageryProvider={imgProvider} > <Camera /> <CameraFlyTo destination={startCoordinates} duration={0} /> </Viewer> </div> }
解决方案
你的核心问题有两个:一是每次用<CameraFlyTo>时硬编码了固定高度(10000),导致更新时强制重置高度;二是setTimeout直接放在组件函数里,会在每次渲染时都创建新的定时器,导致数据更新过于频繁且不可控。
下面是修改后的代码,我会逐一解释关键改动:
import { Cartesian3, Viewer, Camera, OpenStreetMapImageryProvider } from 'resium'; import React, { useState, useEffect, useRef } from 'react'; const imgProvider = new OpenStreetMapImageryProvider({ url: 'https://a.tile.openstreetmap.org/' }); function TrackingMap(props) { const [data, setData] = useState({ longitude: 4.3571, latitude: 52.0116 }); // 保存相机实例的ref,用于获取当前相机的状态 const cameraRef = useRef(null); // 保存定时器ID,用于清理 const timerRef = useRef(null); // 处理定时移动逻辑,用useEffect确保只在组件挂载时启动一次 useEffect(() => { timerRef.current = setInterval(() => { setData(old => ({ longitude: old.longitude + 0.00025, latitude: old.latitude })); }, 200); // 组件卸载时清理定时器 return () => clearInterval(timerRef.current); }, []); // 当目标点更新时,调整相机位置但保留当前高度 useEffect(() => { if (!cameraRef.current) return; const camera = cameraRef.current; // 获取当前相机的高度(从椭球面到相机的距离) const currentHeight = Cartesian3.magnitude(camera.position); // 计算新的目标位置:使用更新后的经纬度,保留当前高度 const newDestination = Cartesian3.fromDegrees(data.longitude, data.latitude, currentHeight); // 平滑移动相机,duration可以根据需求调整,设为0就是瞬间移动 camera.flyTo({ destination: newDestination, duration: 0.2, // 稍微加一点动画会更自然,不需要可以设为0 easingFunction: Cesium.EasingFunction.LINEAR }); }, [data]); return ( <div> <Viewer full resolutionScale={0.5} baseLayerPicker={true} fullscreenButton={false} homeButton={false} sceneModePicker={true} selectionIndicator={true} timeline={false} animation={false} geocoder={false} imageryProvider={imgProvider} > {/* 通过ref获取相机实例 */} <Camera ref={cameraRef} /> {/* 移除原来的CameraFlyTo组件,改用手动控制相机 */} </Viewer> </div> ); } export default TrackingMap;
关键改动说明:
- 使用
useRef保存相机实例:通过<Camera ref={cameraRef}>获取相机对象,这样我们能随时读取当前相机的位置和高度,而不是每次都用固定值。 - 用
setInterval替代重复的setTimeout:把定时逻辑放在useEffect里,确保组件挂载时只启动一次定时器,避免每次渲染都创建新的定时器导致数据疯狂更新,同时在组件卸载时清理定时器防止内存泄漏。 - 动态保留当前相机高度:当目标点
data更新时,先获取当前相机的高度(通过Cartesian3.magnitude(camera.position)计算),然后用这个高度构建新的目标位置,这样缩放后的高度就会被保留。 - 手动调用相机的
flyTo方法:不再依赖<CameraFlyTo>组件(它每次渲染都会触发一次飞行动作,容易覆盖手动调整的状态),而是直接调用相机实例的flyTo方法,更灵活地控制相机行为。
这样修改后,你就能实现:目标点移动时相机自动跟踪,同时手动缩放调整的高度会被保留,不会被强制重置。
内容的提问来源于stack exchange,提问作者paul23
相关产品推荐
相关产品推荐

