前言

CesiumJS中的Camera用来控制场景的视图。目前有很多方法可以操作Camera,比如旋转(rotate)、缩放(zoom)、平移(pan)和飞到目的地(flyTo)。CesiumJS亦有鼠标和触摸时间用来处理与Camera的交互,还有API以编程的方式操作摄像机。

默认Camera行为

打开Sandcastle中的Hello World样例来体验默认的相机控制,默认操作方式如下:
鼠标操作3D2DColumbus视角Left click + dragRotate around the globeTranslate over the mapTranslate over the mapRight click + dragZoom in and outZoom in and outZoom in and outMiddle wheel scrollingZoom in and outZoom in and outZoom in and outMiddle click + dragTilt the globeNo actionTilt the map鼠标操作3D2DColumbus视角左键 + 拖拽旋转地球在地图上移动在地图上移动右键 + 拖拽缩放缩放缩放中键滚轮缩放缩放缩放中键 + 拖拽倾斜地球无操作倾斜地球

使用setView函数设置Camera的位置和方向。destination 参数可以是Cesium.Cartesian3或者Cesium.Rectangle。orientation可以是heading/pitch/roll或者direction/up。航向角、俯仰角和横滚角以弧度定义。

  1. camera.setView({
  2. destination: new Cesium.Cartesian3(x, y, z),
  3. orientation: {
  4. heading: headingAngle,
  5. pitch: pitchAngle,
  6. roll: rollAngle
  7. }
  8. });
  9. viewer.camera.setView({
  10. destination: Cesium.Rectangle.fromDegrees(west, south, east, north),
  11. orientation: {
  12. }
  13. })

上述的所有参数都是可选的。如果未指定,参数值将被设为默认值用户当前Camera的位置和方向。

自定义Camera鼠标和键盘事件

创建我们自己的时间控制,根据鼠标的朝向用于控制Camera的朝向,键盘控制Camera的向前、向左、向右、向上以及向下。首先从禁用默认事件操作开始。代码如下

  1. var scane = viewer.scene
  2. var canvas = viewer.canvas
  3. canvas.setAttribute('tabIndex', '0') // need to put focus on the canvas
  4. canvas.onclick = function() {
  5. canvas.focus()
  6. }
  7. var ellipsoid = viewer.scene.globe.ellipsoid
  8. // disable the default event handlers
  9. scene.screenSpaceCameraController.enableRotae = false
  10. scene.screenSpaceCameraController.enableTranslate = false
  11. scene.screenSpaceCameraController.enableZoom = false
  12. scene.screenSpaceCameraController.enableTilt = false
  13. scene.screenSpaceCameraController.enableLook = false

创建变量记录当前鼠标位置,然后标记并跟随Camera移动轨迹

var startMousePosition,mousePosition
var flags = {
    looking: false,
  moveForward: false,
  moveBackward: false,
  moveUp: false,
  moveDown: false,
  moveLeft: false,
  moveRight: false
}

添加一个事件控制用户设置标记,当鼠标左键被点击的时候,记录当前垫饥位置

var handler = new Cesium.ScreenSpaceEventHandler(canvas)
handler.setInputAction(function(movement) {
    flags.looking = true
  mousePosition = startMousePosition = Cesium.Cartesian3.clone(movenent.position)
}, Cesium.ScreenSpaceEventType.LEFT_DOWN)
handler.setInputAction(function(movememnt){
  mousePosition = movement.endPosition
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
handler.setInputAction(function(position) {
    flags.looking = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);

创建键盘事件控制用户切换Camera移动标记。我们为下列按键和行为设置了标记:

  1. w Camera向前。
  2. s Camera向后。
  3. a Camera向左。
  4. d Camera向右。
  5. q Camera向上。
  6. e Camera向下。 ```javascript function getFlagForKeyCode(keyCode) { switch (keyCode) { case ‘W’.charCodeAt(0):
     return 'moveForward';
    
    case ‘S’.charCodeAt(0):
     return 'moveBackward';
    
    case ‘Q’.charCodeAt(0):
     return 'moveUp';
    
    case ‘E’.charCodeAt(0):
     return 'moveDown';
    
    case ‘D’.charCodeAt(0):
     return 'moveRight';
    
    case ‘A’.charCodeAt(0):
     return 'moveLeft';
    
    default:
     return undefined;
    
    } }

document.addEventListener(‘keydown’, function(e) { var flagName = getFlagForKeyCode(e.keyCode); if (typeof flagName !== ‘undefined’) { flags[flagName] = true; } }, false);

document.addEventListener(‘keyup’, function(e) { var flagName = getFlagForKeyCode(e.keyCode); if (typeof flagName !== ‘undefined’) { flags[flagName] = false; } }, false);

现在当标记表明事件发生为true是,我们更新(update)camera。我们新增**_onTick_的监听事件在clock中:
```javascript
viewer.clock.onTick.addEventListener(function(clock) {
    var camera = viewer.camera;
});

接下来,我们让Camera指向鼠标指向的方向。在变量声明之后添加下列代码到事件监听函数:

if (flags.looking) {
    var width = canvas.clientWidth;
    var height = canvas.clientHeight;

    // Coordinate (0.0, 0.0) will be where the mouse was clicked.
    var x = (mousePosition.x - startMousePosition.x) / width;
    var y = -(mousePosition.y - startMousePosition.y) / height;

    var lookFactor = 0.05;
    camera.lookRight(x * lookFactor);
    camera.lookUp(y * lookFactor);
}

lookRightlookUp只需要一个角度参数用于表示旋转的角度。我们将鼠标坐标转换为范围(-1.0,1.0),坐标(0.0,0.0)位于画布的中心。鼠标距中心的距离决定了旋转的速度。靠近中心的位置移动Camera的速度较慢,而远离中心的位置移动Camera的速度较快。
最后,添加代码用于移动Camera的位置。然后添加下列代码到事件响应函数:

// Change movement speed based on the distance of the camera to the surface of the ellipsoid.
var cameraHeight = ellipsoid.cartesianToCartographic(camera.position).height;
var moveRate = cameraHeight / 100.0;

if (flags.moveForward) {
    camera.moveForward(moveRate);
}
if (flags.moveBackward) {
    camera.moveBackward(moveRate);
}
if (flags.moveUp) {
    camera.moveUp(moveRate);
}
if (flags.moveDown) {
    camera.moveDown(moveRate);
}
if (flags.moveLeft) {
    camera.moveLeft(moveRate);
}
if (flags.moveRight) {
    camera.moveRight(moveRate);
}

moveForwardmoveBackwardmoveUpmoveDownmoveLeftmoveRight方法只需要一个距离参数(米)用于移动Camera的距离。当每一个按键被按下时,Camera就会在球体表面移动固定的距离。Camera离地面越近,移动的速度就越慢。
完整的代码如下:

ar viewer = new Cesium.Viewer('cesiumContainer');

var scene = viewer.scene;
var canvas = viewer.canvas;
canvas.setAttribute('tabindex', '0'); // needed to put focus on the canvas
canvas.onclick = function() {
    canvas.focus();
};
var ellipsoid = viewer.scene.globe.ellipsoid;

// disable the default event handlers
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableTranslate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.screenSpaceCameraController.enableTilt = false;
scene.screenSpaceCameraController.enableLook = false;

var startMousePosition;
var mousePosition;
var flags = {
    looking : false,
    moveForward : false,
    moveBackward : false,
    moveUp : false,
    moveDown : false,
    moveLeft : false,
    moveRight : false
};

var handler = new Cesium.ScreenSpaceEventHandler(canvas);

handler.setInputAction(function(movement) {
    flags.looking = true;
    mousePosition = startMousePosition = Cesium.Cartesian3.clone(movement.position);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

handler.setInputAction(function(movement) {
    mousePosition = movement.endPosition;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

handler.setInputAction(function(position) {
    flags.looking = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);

function getFlagForKeyCode(keyCode) {
    switch (keyCode) {
    case 'W'.charCodeAt(0):
        return 'moveForward';
    case 'S'.charCodeAt(0):
        return 'moveBackward';
    case 'Q'.charCodeAt(0):
        return 'moveUp';
    case 'E'.charCodeAt(0):
        return 'moveDown';
    case 'D'.charCodeAt(0):
        return 'moveRight';
    case 'A'.charCodeAt(0):
        return 'moveLeft';
    default:
        return undefined;
    }
}

document.addEventListener('keydown', function(e) {
    var flagName = getFlagForKeyCode(e.keyCode);
    if (typeof flagName !== 'undefined') {
        flags[flagName] = true;
    }
}, false);

document.addEventListener('keyup', function(e) {
    var flagName = getFlagForKeyCode(e.keyCode);
    if (typeof flagName !== 'undefined') {
        flags[flagName] = false;
    }
}, false);

viewer.clock.onTick.addEventListener(function(clock) {
    var camera = viewer.camera;

    if (flags.looking) {
        var width = canvas.clientWidth;
        var height = canvas.clientHeight;

        // Coordinate (0.0, 0.0) will be where the mouse was clicked.
        var x = (mousePosition.x - startMousePosition.x) / width;
        var y = -(mousePosition.y - startMousePosition.y) / height;

        var lookFactor = 0.05;
        camera.lookRight(x * lookFactor);
        camera.lookUp(y * lookFactor);
    }

    // Change movement speed based on the distance of the camera to the surface of the ellipsoid.
    var cameraHeight = ellipsoid.cartesianToCartographic(camera.position).height;
    var moveRate = cameraHeight / 100.0;

    if (flags.moveForward) {
        camera.moveForward(moveRate);
    }
    if (flags.moveBackward) {
        camera.moveBackward(moveRate);
    }
    if (flags.moveUp) {
        camera.moveUp(moveRate);
    }
    if (flags.moveDown) {
        camera.moveDown(moveRate);
    }
    if (flags.moveLeft) {
        camera.moveLeft(moveRate);
    }
    if (flags.moveRight) {
        camera.moveRight(moveRate);
    }
});