Closed FirokOtaku closed 1 year ago
在事件内的event中可能没有相关值,是ceisum本身的回调,可以取 map.camera内值或调用map相关方法 即可获取相关参数
另外在这些事件的监听器内尝试调用
map.camera.setView
等 API 均效果不佳, 会产生闪屏 / 视角错位等错误
当前视角参数的获取没有问题, 但是在监听器方法内直接操作 API 会导致视角错位.
function onCameraChanged(event)
{
const { lat, lng, alt, heading, pitch } = Map.getCameraView()
const paramView = {
destination: Cesium.Cartesian3.fromDegrees(lng, lat, alt),
orientation: { heading, pitch }
}
Map.camera.setView(paramView) // 把当前的经纬度等参数原样写回都会错位
}
map.on(mars3d.EventType.cameraChanged, onCameraChanged, this)
有具体需求不,我分析下如果写合适
现有需求限制摄像机视角锁定在固定经纬度 / 固定朝向范围 / 固定高度范围
这就是具体需求.
这边的应用场景是用来显示雕像 / 告示牌这样的模型, 相机视角会始终以模型为中心 (朝向这个模型), 另外不论如何用户拖动, 都只允许从正面 180 度 (或者其它自定义的) 范围查看模型.
可能需要从鼠标交互事件入手,而不是相机变动事件
下面代码是片段性的关键逻辑,可以参考下
setPitchRange(max, min = -90) {
map.scene.screenSpaceCameraController.inertiaSpin = 0
map.scene.screenSpaceCameraController.inertiaTranslate = 0
map.on(mars3d.EventType.mouseDown, this._setPitchRange_rightDownHandler, this)
map.on(mars3d.EventType.mouseUp, this._setPitchRange_rightUpHandler, this)
}
_setPitchRange_rightDownHandler(evnet) {
map.on(mars3d.EventType.mouseMove, this._setPitchRange_mouseMoveHandler, this)
map.on(mars3d.EventType.cameraChanged, this._setPitchRange_cameraChangedHandler, this)
}
_setPitchRange_rightUpHandler(evnet) {
map.scene.screenSpaceCameraController.enableTilt = true
map.off(mars3d.EventType.mouseMove, this._setPitchRange_mouseMoveHandler, this)
map.off(mars3d.EventType.cameraChanged, this._setPitchRange_cameraChangedHandler, this)
}
_setPitchRange_mouseMoveHandler(evnet) {
let enableTilt = true
// const isUp = evnet.endPosition.y < evnet.startPosition.y
// if (isUp && map.camera.pitch > this._pitch_max) {
// enableTilt = false
// } else if (!isUp && map.camera.pitch < this._pitch_min) {
// enableTilt = false
// } else {
// enableTilt = true
// }
map.scene.screenSpaceCameraController.enableTilt = enableTilt
}
_setPitchRange_cameraChangedHandler(evnet) {
// if (map.scene.mode !== Cesium.SceneMode.SCENE3D) {
// return
// }
// if (map.camera.positionCartographic.height > map.scene.screenSpaceCameraController.minimumCollisionTerrainHeight) {
// return
// }
// let pitch = map.camera.pitch
if (pitch > this._pitch_max || pitch < this._pitch_min) {
// map.scene.screenSpaceCameraController.enableTilt = false
// // clamp the pitch
// if (pitch > this._pitch_max) {
// pitch = this._pitch_max
// } else if (pitch < this._pitch_min) {
// pitch = this._pitch_min
// }
// const destination = Cesium.Cartesian3.fromRadians(
// map.camera.positionCartographic.longitude,
// map.camera.positionCartographic.latitude,
// Math.max(map.camera.positionCartographic.height, this._pitch_minHeight)
// )
map.camera.cancelFlight()
map.camera.setView({
destination: destination,
orientation: {
pitch: pitch
}
})
map.scene.screenSpaceCameraController.enableTilt = true
}
}
稍微加了点逻辑, 传给 map.camera.setView
的参数里已经对 pitch
等值限制了范围, 但是看起来不是很好用.
测试的时候视角还是会超出预定范围.
好的时候只超出范围一点点, 坏的时候视角整个就飞到不知道什么角度了.
有时候 pitch
会变成正数, 变成从地底朝上看;
另外相机视角离地高度也没法没法限制最小离地距离.
有点奇怪. 是我哪里理解有误吗?
function range(value = NaN, min = NaN, max = NaN)
{
value = +value
if(isNaN(value)) return NaN
min = +min
max = +max
if(!isNaN(min) && value <= min) return min
if(!isNaN(max) && value >= max) return max
return value
}
let cameraRange = {
pitchMin: -90,
pitchMax: -10,
headingMin: 90,
headingMax: 180,
altMin: 300,
altMax: 500,
}
function setCameraRange({ pitchMin, pitchMax, headingMin, headingMax, altMin, altMax, })
{
map.scene.screenSpaceCameraController.inertiaSpin = 0
map.scene.screenSpaceCameraController.inertiaTranslate = 0
if(pitchMin != null) cameraRange.pitchMin = pitchMin
if(pitchMax != null) cameraRange.pitchMax = pitchMax
if(headingMin != null) cameraRange.headingMin = headingMin
if(headingMax != null) cameraRange.headingMax = headingMax
if(altMin != null) cameraRange.altMin = altMin
if(altMax != null) cameraRange.altMax = altMax
}
function onMouseDown()
{
map.on(mars3d.EventType.mouseMove, onMouseMove, this)
map.on(mars3d.EventType.cameraChanged, onCameraChange, this)
}
function onMouseUp()
{
map.scene.screenSpaceCameraController.enableTilt = true
map.off(mars3d.EventType.mouseMove, onMouseMove, this)
map.off(mars3d.EventType.cameraChanged, onCameraChange, this)
}
// 用来在 UI 显示当前值的测试变量
const LNG = ref(0), LAT = ref(0), HEADING = ref(0), PITCH = ref(0), ALT = ref(0)
function onMouseMove(event)
{
const { heading, pitch } = map.getCameraView()
const mcp = map.camera.positionCartographic
const lng = mcp.longitude, lat = mcp.latitude, alt = mcp.height
LNG.value = lng
LAT.value = lat
ALT.value = alt
HEADING.value = heading
PITCH.value = pitch
const { pitchMin, pitchMax, headingMin, headingMax, altMin, altMax } = cameraRange
let enableUpDown
if (event.endPosition.y < event.startPosition.y)
enableUpDown = !(
(!isNaN(pitchMax) && pitch > pitchMax) ||
(!isNaN(altMax) && alt > altMax)
)
else if (event.endPosition.y > event.startPosition.y)
enableUpDown = !(
(!isNaN(pitchMin) && pitch < pitchMin) ||
(!isNaN(altMin) && alt < altMin)
)
else
enableUpDown = true
let enableLeftRight
if(event.endPosition.x > event.startPosition.x)
enableLeftRight = !(
(!isNaN(headingMax) && heading > headingMax)
)
else if(event.endPosition.x < event.startPosition.x)
enableLeftRight = !(
(!isNaN(headingMin) && heading < headingMin)
)
else
enableLeftRight = true
map.scene.screenSpaceCameraController.enableTilt = enableUpDown && enableLeftRight
}
function onCameraChange(event)
{
const { pitchMax, pitchMin, headingMax, headingMin, altMin, altMax } = cameraRange
let { pitch, heading } = map.camera
if (!map.scene.screenSpaceCameraController.enableTilt)
{
pitch = range(pitch, pitchMin, pitchMax)
map.camera.cancelFlight()
map.camera.setView({
destination: Cesium.Cartesian3.fromRadians(
map.camera.positionCartographic.longitude,
map.camera.positionCartographic.latitude,
range(map.camera.positionCartographic.height, altMin, altMax)
),
orientation: {
pitch: range(pitch, pitchMin, pitchMax),
heading: range(heading, headingMin, headingMax),
}
})
map.scene.screenSpaceCameraController.enableTilt = true
}
}
onMounted(async () => {
map.scene.screenSpaceCameraController.inertiaSpin = 0
map.scene.screenSpaceCameraController.inertiaTranslate = 0
map.on(mars3d.EventType.mouseDown, onMouseDown, this)
map.on(mars3d.EventType.mouseUp, onMouseUp, this)
})
onMounted(async () => {
let mapOptions = {
scene: {
shadows: false, //是否启用日照阴影
removeDblClick: true, //是否移除Cesium默认的双击事件
//以下是Cesium.Viewer所支持的options【控件相关的写在另外的control属性中】
sceneMode: 3, //3等价于Cesium.SceneMode.SCENE3D,
//以下是Cesium.Scene对象相关参数
showSun: false, //是否显示太阳
showMoon: false, //是否显示月亮
showSkyBox: true, //是否显示天空盒
showSkyAtmosphere: false, //是否显示地球大气层外光圈
fog: false, //是否启用雾化效果
fxaa: true, //是否启用抗锯齿
//以下是Cesium.Globe对象相关参数
globe: {
depthTestAgainstTerrain: false, //是否启用深度监测
baseColor: '#546a53', //地球默认背景色
showGroundAtmosphere: false, //是否在地球上绘制的地面大气
enableLighting: false //是否显示昼夜区域
},
//以下是Cesium.ScreenSpaceCameraController对象相关参数
cameraController: {
zoomFactor: 5, //鼠标滚轮放大的步长参数
minimumZoomDistance: 50, //地球放大的最小值(以米为单位)
maximumZoomDistance: 100, //地球缩小的最大值(以米为单位)
minimumCollisionTerrainHeight: 100,
enableRotate: true , //2D和3D视图下,是否允许用户旋转相机
enableTranslate: false, //2D和哥伦布视图下,是否允许用户平移地图
enableTilt: true, // 3D和哥伦布视图下,是否允许用户倾斜相机
enableZoom: true, // 是否允许 用户放大和缩小视图
enableCollisionDetection: true //是否允许 地形相机的碰撞检测
},
},
control: {
baseLayerPicker: false, //basemaps底图切换按钮
homeButton: true, //视角复位按钮
sceneModePicker: false, //二三维切换按钮
navigationHelpButton: false, //帮助按钮
fullscreenButton: false, //全屏按钮
},
basemaps: []
}
map = new mars3d.Map(
'mars3dContainer',
mapOptions,
)
map.fixedLight = true
map = markRaw(map)
const tiles3dLayer = new mars3d.layer.TilesetLayer({
name: "县城社区",
url: "//data.mars3d.cn/3dtiles/qx-shequ/tileset.json",
position: { alt: 11.5 },
maximumScreenSpaceError: 1,
maximumMemoryUsage: 1024,
dynamicScreenSpaceError: true,
cullWithChildrenBounds: false,
skipLevelOfDetail: true,
preferLeaves: true,
center: { lat: 28.439577, lng: 119.476925, alt: 229, heading: 57, pitch: -29 },
enableDebugWireframe: true, // 是否可以进行三角网的切换显示
flyTo: true
})
map.addLayer(tiles3dLayer)
tiles3dLayer.flyTo().finally(() => {})
map.on(mars3d.EventType.mouseDown, onMouseDown, this)
map.on(mars3d.EventType.mouseUp, onMouseUp, this)
window.map = map
})
好吧, 想了想, 完全换了一种写法, 禁用 mars3d 所有的视角移动选项,
cameraController: {
enableRotate: false , //2D和3D视图下,是否允许用户旋转相机
enableTranslate: false, //2D和哥伦布视图下,是否允许用户平移地图
enableTilt: false, // 3D和哥伦布视图下,是否允许用户倾斜相机
enableZoom: false, // 是否允许 用户放大和缩小视图
enableCollisionDetection: false //是否允许 地形相机的碰撞检测
}
然后监听 mouseUp
, mouseDown
, mouseMove
事件,
手动根据鼠标移动的偏移角度用三角函数算相机的空间位置和朝向,
把算出来的东西 map.setCameraView
到 mars3d 里,
大致实现需要用的功能了.
新问题来了, 翻了半天 wheel
事件实例的字段,
没找到滚轮滚动值 (判断滚轮向上 / 向下滚动),
镜头视角缩放功能少一个常见的快捷键.
所以说传入监听器的事件实例能不能把原生 Web 事件实例加上啊,
哪怕不允许直接访问原生 MouseEvent
对象,
把里面字段复制一份让监听器方法能读到, 有些事能好办很多.
另外请问有没有对 各类型事件触发的时候会传给监听器什么结构的事件实例参数 的描述文档?
现有文档 (Event, Global#EventType, BaseClass#fire) 里没找到相关信息. 现在只知道发生 xx 事件之后 xx 监听器方法就会被调用, 不知道调用的时候会传给监听器方法什么参数, 还得现翻 F12 控制台挨个字段瞅.
cameraController
建议看看ScreenSpaceCameraController类代码,是cesium对鼠标交互控制相机的所有逻辑。
另外请问有没有对 各类型事件触发的时候会传给监听器什么结构的事件实例参数 的描述文档?
现有文档 (Event, Global#EventType, BaseClass#fire) 里没找到相关信息. 现在只知道发生 xx 事件之后 xx 监听器方法就会被调用, 不知道调用的时候会传给监听器方法什么参数, 还得现翻 F12 控制台挨个字段瞅.
目前还没有,需要F12打印event后看下值。
目前还没有,需要F12打印event后看下值。
好的
现有需求限制摄像机视角锁定在固定经纬度 / 固定朝向范围 / 固定高度范围.
尝试监听如下方法:
map.on(mars3d.EventType.cameraMoveStart, fun, this)
map.on(mars3d.EventType.cameraChanged, fun, this)
map.on(mars3d.EventType.cameraMoveEnd, fun, this)
发现传入
fun
内的参数不是undefined
就是一个number
类型值, 无法直接调整目标相机视角等参数. 另外在这些事件的监听器内尝试调用map.camera.setView
等 API 均效果不佳, 会产生闪屏 / 视角错位等错误.请问有无方法可以在监听器方法内获取事件实例? 或有无方法获取到即将提交到事件总线的事件实例? 另外能否修改获取到的事件实例成员参数以调整本次事件过程?