在 Cesium 1.138 中,限制相机在特定 Level 之间展示地图,核心是用 screenSpaceCameraController 的距离限制 + 层级 - 高度映射,再配合瓦片层级约束,实现 "只能在指定层级区间内缩放"。
一、核心原理
Cesium 没有直接 "按 Level 锁相机" 的 API,但可以:
- 先映射:把你要的 minLevel / maxLevel 换算成相机到地球的最小 / 最大距离(米)。
- 再限制:用 minimumZoomDistance / maximumZoomDistance 锁死相机高度,让用户无法超出该区间。
- 配合瓦片:同时设置 minimumLevel / maximumLevel,让超出层级的瓦片不加载,视觉上更干净。
二、完整实现代码(可直接运行)
const viewer = new Cesium.Viewer("cesiumContainer", {
// 1. 先在 Viewer 上约束瓦片加载层级(视觉兜底)
minimumLevel: 5,
maximumLevel: 12,
rectangle: Cesium.Rectangle.fromDegrees(-180, -90, 180, 90)
});
// 2. 定义你要锁定的层级区间
const TARGET_MIN_LEVEL = 5; // 最小层级(最"远")
const TARGET_MAX_LEVEL = 12; // 最大层级(最"近")
// 3. 层级 → 相机距离(米)映射(可根据你的底图微调)
// 示例值:全球场景下,Level 5 ≈ 8000km,Level 12 ≈ 10km
const levelToDistance = (level) => {
// 经验公式:距离 ≈ 地球半径 × 2π / (2^level)
const earthRadius = 6378137;
return (earthRadius * 2 * Math.PI) / Math.pow(2, level);
};
const minDistance = levelToDistance(TARGET_MIN_LEVEL); // 最大缩放距离(不能再远)
const maxDistance = levelToDistance(TARGET_MAX_LEVEL); // 最小缩放距离(不能再近)
// 4. 锁死相机缩放范围(核心)
const controller = viewer.scene.screenSpaceCameraController;
controller.minimumZoomDistance = maxDistance; // 相机最近距离(对应最高层级)
controller.maximumZoomDistance = minDistance; // 相机最远距离(对应最低层级)
// 5. 可选:监听相机,实时打印当前层级(调试用)
viewer.scene.preRender.addEventListener(() => {
const camera = viewer.camera;
const cartographic = camera.positionCartographic;
const height = cartographic.height;
// 反向计算当前层级(近似)
const currentLevel = Math.log2((6378137 * 2 * Math.PI) / height);
console.log(`当前层级 ≈ ${currentLevel.toFixed(1)},高度 ≈ ${(height/1000).toFixed(0)}km`);
});
三、关键参数说明
- screenSpaceCameraController 距离限制(强制锁相机)
- minimumZoomDistance:相机到地球表面的最小距离(米) → 对应 最高层级(最大放大)。用户无法再拉近。
- maximumZoomDistance:相机到地球表面的最大距离(米) → 对应 最低层级(最大缩小)。用户无法再拉远。
-
层级 ↔ 距离映射(经验公式)
// 距离 ≈ 地球周长 / 2^level
const levelToDistance = (level) => (6378137 * 2 * Math.PI) / Math.pow(2, level);
该公式是近似值,不同底图、不同纬度会有偏差,建议根据实际场景微调。
示例参考(全球场景):
- Level 5 → ~8,000,000 m(8000 km)
- Level 10 → ~250,000 m(250 km)
- Level 15 → ~8,000 m(8 km)
- Level 18 → ~1,000 m(1 km)
- 与 minimumLevel/maximumLevel 的区别
- minimumLevel/maximumLevel:控制瓦片是否加载,超出层级的瓦片不请求,但相机仍可自由缩放(只是看不到底图)。
- minimumZoomDistance/maximumZoomDistance:控制相机物理位置,强制用户无法缩放超出指定距离,从交互上彻底锁定层级区间。
四、进阶:更精准的层级锁定(可选)
如果需要严格按瓦片层级锁死(比如必须停在整数 Level),可以监听相机移动,超出时自动 "吸附" 到最近的合法层级:
// 吸附到指定层级区间
const clampToLevelRange = (minLevel, maxLevel) => {
const camera = viewer.camera;
const cartographic = camera.positionCartographic;
const height = cartographic.height;
const currentLevel = Math.log2((6378137 * 2 * Math.PI) / height);
if (currentLevel < minLevel) {
// 缩得太远 → 强制拉回 minLevel 高度
const targetHeight = levelToDistance(minLevel);
camera.setView({
destination: Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
targetHeight
)
});
} else if (currentLevel > maxLevel) {
// 放得太近 → 强制推回 maxLevel 高度
const targetHeight = levelToDistance(maxLevel);
camera.setView({
destination: Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
targetHeight
)
});
}
};
// 每帧检查并吸附
viewer.scene.preRender.addEventListener(() => {
clampToLevelRange(TARGET_MIN_LEVEL, TARGET_MAX_LEVEL);
});
五、常见问题与优化
- 层级与距离不匹配:不同底图(如高德、天地图)的瓦片分辨率不同,levelToDistance 需微调。
- 地形影响:开启地形后,相机高度是离地高度,而非到椭球面的距离,映射公式会有偏差,建议适当放宽距离范围。
- 性能:preRender 事件每帧触发,逻辑要轻量;复杂场景可降低检查频率。