作者:zhouyp
目录
一、前言
在iClient3D for WebGL中限制相机高度及范围可以提高用户体验,防止用户缩放到不合适的高度或超出预定的地理范围,一起来看看如何实现吧!

二、关键代码
监听scene.preRender事件,该事件将会在每帧渲染之前触发,事件回调中我们获取到当前帧的相机位置,当超出高度和范围限制条件时将相机位置修改为限制条件阈值。详情代码如需:
let minHeight = 400, maxHeight = 15000;
let west = 13.000, south = 47.785, east = 13.105, north = 47.858;
viewer.scene.preRender.addEventListener(function () {
updateCameraPositionWithConstrants(
viewer.camera,
minHeight,
maxHeight,
west,
east,
north,
south);
});
// 获取约束条件下相机位置
function updateCameraPositionWithConstrants(currentCamera, minHeight, maxHeight, west, east, north, south) {
//获取 当前相机经度纬度高度
let cartographic = SuperMap3D.Cartographic.fromCartesian(currentCamera.position);
let longitude = SuperMap3D.Math.toDegrees(cartographic.longitude);
let latitude = SuperMap3D.Math.toDegrees(cartographic.latitude);
let height = cartographic.height;
let trigger = false;
// 判断相机位置是否触发高度约束,若触发将其值修改为高度约束阈值
if (height < minHeight) {
height = minHeight;
trigger = true;
}
if (height > maxHeight) {
height = maxHeight;
trigger = true;
}
// 判断相机位置是否触发范围约束,若触发将其值修改为范围约束阈值
if (longitude < west) {
longitude = Math.max(longitude, west);
trigger = true;
}
if (longitude > east) {
longitude = Math.min(longitude, east);
trigger = true;
}
if (latitude > north) {
latitude = Math.min(latitude, north);
trigger = true;
}
if (latitude < south) {
latitude = Math.max(latitude, south);
trigger = true;
}
if (trigger) {
currentCamera.position = SuperMap3D.Cartesian3.fromDegrees(longitude, latitude, height);;
}
}
三、完整示例代码
注:复制后放入iClient3D for WebGL 范例沙盒中可以直接运行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>限制相机高度及范围</title>
<link href="../../Build/SuperMap3D/Widgets/widgets.css" rel="stylesheet">
<link href="./css/pretty.css" rel="stylesheet">
<link href="./css/style.css" rel="stylesheet">
<link href="./style/cloudAndSky.css" rel="stylesheet">
<script type="text/javascript" src="./js/jquery.min.js"></script>
<script src="./js/tooltip.js"></script>
<script src="./js/spectrum.js"></script>
<script src="./js/config.js"></script>
<script type="text/javascript" src="../../Build/SuperMap3D/SuperMap3D.js"></script>
</head>
<body>
<div id="Container"></div>
<div id='loadingbar' class="spinner">
<div class="spinner-container container1">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container2">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container3">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
</div>
<div id="toolbar" class="param-container tool-bar">
<div class="titleBox">
<div class="titl">限制相机高度及范围</div>
</div>
<div class="param-item inputBox showCloud">
<label class="lable">最大高度</label>
<input id="maxVisual" type="number" value="15000" style="text-align: center">
</div>
<div class="param-item inputBox showCloud">
<label class="lable">最小高度</label>
<input id="minVisual" type="number" value="400" style="text-align: center">
</div>
<div class="param-item inputBox showCloud">
<label class="lable">限制范围</label>
<label class="lable">
<div style="height: 1px;margin-top: 16px;background: red;
"></div>
</label>
</div>
<div class="param-item inputBox showCloud">
<label class="lable">相机高度(米)</label>
<label class="lable" id="currentH"></label>
</div>
<div class="param-item inputBox showCloud">
<label class="lable">相机经度(度)</label>
<label class="lable" id="currentLon"></label>
</div>
<div class="param-item inputBox showCloud">
<label class="lable">相机纬度(度)</label>
<label class="lable" id="currentLat"></label>
</div>
</div>
<script type="text/javascript">
function onload(SuperMap3D) {
let viewer = new SuperMap3D.Viewer('Container', {
contextOptions: {
contextType: Number(2), // Webgl2:2 ; WebGPU:3
}
});
viewer.scenePromise.then(function (scene) {
scene.globe.depthTestAgainstTerrain = false;
init(SuperMap3D, scene, viewer);
});
}
function init(SuperMap3D, scene, viewer) {
viewer.resolutionScale = window.devicePixelRatio;
let widget = viewer.Widget;
try {
// 打开三维场景
let promise = scene.open(URL_CONFIG.SCENE_SRSB);
SuperMap3D.when(promise, function (layers) {
layers.forEach(layer => {
layer.selectEnabled = false;
let style = new SuperMap3D.Style3D();
style.bottomAltitude = -58;
layer.style3D = style;
layer.setLodRangeScale(0.1)
layer.refresh();
})
// 获取界面高度范围
let minHeight = parseFloat($("#minVisual").val());
let maxHeight = parseFloat($("#maxVisual").val());
// 监听界面高度范围变化
$("#minVisual").on('input', function () {
minHeight = parseFloat($("#minVisual").val());
maxHeight = parseFloat($("#maxVisual").val());
if (minHeight > maxHeight) {
minHeight = maxHeight
$("#minVisual").val(minHeight)
}
});
$("#maxVisual").on('input', function () {
minHeight = parseFloat($("#minVisual").val());
maxHeight = parseFloat($("#maxVisual").val());
if (maxHeight < minHeight) {
maxHeight = minHeight
$("#maxVisual").val(maxHeight)
}
});
// 约束相机移动范围
let west = 13.000, south = 47.785, east = 13.105, north = 47.858;
let rectangle = SuperMap3D.Rectangle.fromDegrees(west, south, east, north);
viewer.camera.setView({
destination: rectangle
});
viewer.entities.add({
rectangle: {
coordinates: rectangle,
fill: false,
outline: true,
outlineColor: SuperMap3D.Color.RED,
outlineWidth: 10,
},
});
viewer.scene.preRender.addEventListener(function () {
updateCameraPositionWithConstrants(
viewer.camera,
minHeight,
maxHeight,
west,
east,
north,
south);
});
// 获取约束条件下相机位置
function updateCameraPositionWithConstrants(currentCamera, minHeight, maxHeight, west, east, north, south) {
//获取 当前相机经度纬度高度
let cartographic = SuperMap3D.Cartographic.fromCartesian(currentCamera.position);
let longitude = SuperMap3D.Math.toDegrees(cartographic.longitude);
let latitude = SuperMap3D.Math.toDegrees(cartographic.latitude);
let height = cartographic.height;
let trigger = false;
// 判断相机位置是否触发高度约束,若触发将其值修改为高度约束阈值
if (height < minHeight) {
height = minHeight;
trigger = true;
}
if (height > maxHeight) {
height = maxHeight;
trigger = true;
}
// 判断相机位置是否触发范围约束,若触发将其值修改为范围约束阈值
if (longitude < west) {
longitude = Math.max(longitude, west);
trigger = true;
}
if (longitude > east) {
longitude = Math.min(longitude, east);
trigger = true;
}
if (latitude > north) {
latitude = Math.min(latitude, north);
trigger = true;
}
if (latitude < south) {
latitude = Math.max(latitude, south);
trigger = true;
}
if (trigger) {
currentCamera.position = SuperMap3D.Cartesian3.fromDegrees(longitude, latitude, height);;
}
}
// 相机位置发生变化时同步状态到界面
viewer.scene.camera.changed.addEventListener(function () {
let cartographic = SuperMap3D.Cartographic.fromCartesian(viewer.camera.position);
let height = cartographic.height;
let longitude = SuperMap3D.Math.toDegrees(cartographic.longitude);
let latitude = SuperMap3D.Math.toDegrees(cartographic.latitude);
$("#currentH").text(Math.round(height));
$("#currentLon").text(longitude.toFixed(6));
$("#currentLat").text(latitude.toFixed(7));
})
}, function () {
let title = '加载SCP失败,请检查网络连接状态或者url地址是否正确?';
widget.showErrorPanel(title, undefined, e);
});
}
catch (e) {
if (widget._showRenderLoopErrors) {
let title = '渲染时发生错误,已停止渲染。';
widget.showErrorPanel(title, undefined, e);
}
}
$("#loadingbar").remove();
$("#toolbar").show();
}
if (typeof SuperMap3D !== 'undefined') {
window.startupCalled = true;
onload(SuperMap3D);
}
</script>
</body>
</html>