第14章 - 交互与事件处理

第14章:交互与事件处理

14.1 事件处理器

14.1.1 ScreenSpaceEventHandler

// 创建事件处理器
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);

// 鼠标事件类型
const eventTypes = {
    LEFT_DOWN: Cesium.ScreenSpaceEventType.LEFT_DOWN,
    LEFT_UP: Cesium.ScreenSpaceEventType.LEFT_UP,
    LEFT_CLICK: Cesium.ScreenSpaceEventType.LEFT_CLICK,
    LEFT_DOUBLE_CLICK: Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK,
    RIGHT_DOWN: Cesium.ScreenSpaceEventType.RIGHT_DOWN,
    RIGHT_UP: Cesium.ScreenSpaceEventType.RIGHT_UP,
    RIGHT_CLICK: Cesium.ScreenSpaceEventType.RIGHT_CLICK,
    MIDDLE_DOWN: Cesium.ScreenSpaceEventType.MIDDLE_DOWN,
    MIDDLE_UP: Cesium.ScreenSpaceEventType.MIDDLE_UP,
    MIDDLE_CLICK: Cesium.ScreenSpaceEventType.MIDDLE_CLICK,
    MOUSE_MOVE: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
    WHEEL: Cesium.ScreenSpaceEventType.WHEEL,
    PINCH_START: Cesium.ScreenSpaceEventType.PINCH_START,
    PINCH_MOVE: Cesium.ScreenSpaceEventType.PINCH_MOVE,
    PINCH_END: Cesium.ScreenSpaceEventType.PINCH_END
};

// 键盘修饰符
const modifiers = {
    SHIFT: Cesium.KeyboardEventModifier.SHIFT,
    CTRL: Cesium.KeyboardEventModifier.CTRL,
    ALT: Cesium.KeyboardEventModifier.ALT
};

14.1.2 注册事件

// 左键点击
handler.setInputAction(function(click) {
    console.log('点击位置:', click.position);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 鼠标移动
handler.setInputAction(function(movement) {
    console.log('起点:', movement.startPosition);
    console.log('终点:', movement.endPosition);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// 滚轮
handler.setInputAction(function(delta) {
    console.log('滚轮:', delta);
}, Cesium.ScreenSpaceEventType.WHEEL);

// 组合键(Ctrl + 点击)
handler.setInputAction(function(click) {
    console.log('Ctrl + 点击');
}, Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.CTRL);

// 移除事件
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 销毁处理器
handler.destroy();

14.2 对象拾取

14.2.1 基本拾取

handler.setInputAction(function(click) {
    // 拾取第一个对象
    const pickedObject = viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject)) {
        // Entity
        if (pickedObject.id instanceof Cesium.Entity) {
            const entity = pickedObject.id;
            console.log('选中 Entity:', entity.name);
        }
        
        // 3D Tiles
        if (pickedObject.primitive instanceof Cesium.Cesium3DTileset) {
            console.log('选中 3D Tiles');
            if (pickedObject.getProperty) {
                console.log('属性:', pickedObject.getPropertyIds());
            }
        }
        
        // Primitive
        if (pickedObject.primitive instanceof Cesium.Primitive) {
            console.log('选中 Primitive:', pickedObject.id);
        }
    }
    
    // 拾取位置(世界坐标)
    const worldPosition = viewer.scene.pickPosition(click.position);
    if (Cesium.defined(worldPosition)) {
        const cartographic = Cesium.Cartographic.fromCartesian(worldPosition);
        console.log('位置:', {
            lon: Cesium.Math.toDegrees(cartographic.longitude),
            lat: Cesium.Math.toDegrees(cartographic.latitude),
            height: cartographic.height
        });
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 穿透拾取(获取所有对象)
const pickedObjects = viewer.scene.drillPick(click.position);
console.log('拾取对象数:', pickedObjects.length);

14.2.2 高亮与选中

// 高亮管理器
class HighlightManager {
    constructor(viewer) {
        this.viewer = viewer;
        this.highlighted = null;
        this.selected = null;
        this.highlightColor = Cesium.Color.YELLOW.withAlpha(0.5);
        this.selectColor = Cesium.Color.CYAN;
        this.originalColors = new Map();
    }
    
    highlight(object) {
        // 清除之前的高亮
        if (this.highlighted && this.highlighted !== this.selected) {
            this.restoreColor(this.highlighted);
        }
        
        if (object && object !== this.selected) {
            this.saveColor(object);
            this.setColor(object, this.highlightColor);
            this.highlighted = object;
        } else {
            this.highlighted = null;
        }
    }
    
    select(object) {
        // 清除之前的选中
        if (this.selected) {
            this.restoreColor(this.selected);
        }
        
        if (object) {
            this.saveColor(object);
            this.setColor(object, this.selectColor);
            this.selected = object;
        } else {
            this.selected = null;
        }
    }
    
    saveColor(object) {
        if (object.id instanceof Cesium.Entity) {
            const entity = object.id;
            if (entity.polygon) {
                this.originalColors.set(object, entity.polygon.material.getValue().color.clone());
            }
        }
    }
    
    restoreColor(object) {
        const color = this.originalColors.get(object);
        if (color) {
            this.setColor(object, color);
            this.originalColors.delete(object);
        }
    }
    
    setColor(object, color) {
        if (object.id instanceof Cesium.Entity) {
            const entity = object.id;
            if (entity.polygon) {
                entity.polygon.material = color;
            }
        }
    }
}

// 使用
const highlightManager = new HighlightManager(viewer);

handler.setInputAction(function(movement) {
    const object = viewer.scene.pick(movement.endPosition);
    highlightManager.highlight(object);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

handler.setInputAction(function(click) {
    const object = viewer.scene.pick(click.position);
    highlightManager.select(object);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

14.3 绘图工具

14.3.1 点绘制

class PointDrawTool {
    constructor(viewer) {
        this.viewer = viewer;
        this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
        this.points = [];
    }
    
    start(callback) {
        this.handler.setInputAction((click) => {
            const position = this.viewer.scene.pickPosition(click.position);
            if (Cesium.defined(position)) {
                const entity = this.viewer.entities.add({
                    position: position,
                    point: {
                        pixelSize: 10,
                        color: Cesium.Color.RED,
                        outlineColor: Cesium.Color.WHITE,
                        outlineWidth: 2
                    }
                });
                this.points.push(entity);
                
                if (callback) {
                    const cartographic = Cesium.Cartographic.fromCartesian(position);
                    callback({
                        entity: entity,
                        position: {
                            lon: Cesium.Math.toDegrees(cartographic.longitude),
                            lat: Cesium.Math.toDegrees(cartographic.latitude),
                            height: cartographic.height
                        }
                    });
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }
    
    stop() {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }
    
    clear() {
        this.points.forEach(p => this.viewer.entities.remove(p));
        this.points = [];
    }
}

14.3.2 多边形绘制

class PolygonDrawTool {
    constructor(viewer) {
        this.viewer = viewer;
        this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
        this.positions = [];
        this.tempEntities = [];
        this.polygon = null;
    }
    
    start(callback) {
        // 左键添加点
        this.handler.setInputAction((click) => {
            const position = this.viewer.scene.pickPosition(click.position);
            if (Cesium.defined(position)) {
                this.positions.push(position);
                this.addVertex(position);
                this.updatePolygon();
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        
        // 鼠标移动
        this.handler.setInputAction((movement) => {
            if (this.positions.length >= 2) {
                const position = this.viewer.scene.pickPosition(movement.endPosition);
                if (Cesium.defined(position)) {
                    this.updateTempPolygon(position);
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        
        // 右键完成
        this.handler.setInputAction(() => {
            if (this.positions.length >= 3) {
                this.finish(callback);
            }
        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        
        // 双击完成
        this.handler.setInputAction(() => {
            if (this.positions.length >= 3) {
                this.finish(callback);
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    }
    
    addVertex(position) {
        const point = this.viewer.entities.add({
            position: position,
            point: { pixelSize: 8, color: Cesium.Color.RED }
        });
        this.tempEntities.push(point);
    }
    
    updatePolygon() {
        if (this.positions.length < 3) return;
        
        if (this.polygon) {
            this.polygon.polygon.hierarchy = new Cesium.PolygonHierarchy(this.positions);
        } else {
            this.polygon = this.viewer.entities.add({
                polygon: {
                    hierarchy: new Cesium.PolygonHierarchy(this.positions),
                    material: Cesium.Color.YELLOW.withAlpha(0.3),
                    outline: true,
                    outlineColor: Cesium.Color.YELLOW
                }
            });
        }
    }
    
    updateTempPolygon(mousePosition) {
        if (!this.polygon) return;
        const tempPositions = [...this.positions, mousePosition];
        this.polygon.polygon.hierarchy = new Cesium.PolygonHierarchy(tempPositions);
    }
    
    finish(callback) {
        this.stop();
        
        // 清理临时点
        this.tempEntities.forEach(e => this.viewer.entities.remove(e));
        this.tempEntities = [];
        
        if (callback) {
            const coordinates = this.positions.map(p => {
                const c = Cesium.Cartographic.fromCartesian(p);
                return [Cesium.Math.toDegrees(c.longitude), Cesium.Math.toDegrees(c.latitude)];
            });
            callback({ polygon: this.polygon, coordinates: coordinates });
        }
    }
    
    stop() {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    }
    
    clear() {
        this.tempEntities.forEach(e => this.viewer.entities.remove(e));
        if (this.polygon) this.viewer.entities.remove(this.polygon);
        this.positions = [];
        this.tempEntities = [];
        this.polygon = null;
    }
}

// 使用
const drawTool = new PolygonDrawTool(viewer);
drawTool.start((result) => {
    console.log('绘制完成:', result.coordinates);
});

14.4 相机交互控制

// 禁用默认交互
const controller = viewer.scene.screenSpaceCameraController;
controller.enableRotate = false;
controller.enableTranslate = false;
controller.enableZoom = false;
controller.enableTilt = false;
controller.enableLook = false;

// 自定义交互
let isDragging = false;
let lastPosition = null;

handler.setInputAction((click) => {
    isDragging = true;
    lastPosition = click.position.clone();
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

handler.setInputAction((movement) => {
    if (isDragging && lastPosition) {
        const dx = movement.endPosition.x - lastPosition.x;
        const dy = movement.endPosition.y - lastPosition.y;
        
        // 自定义平移逻辑
        viewer.camera.moveRight(dx * 100);
        viewer.camera.moveDown(dy * 100);
        
        lastPosition = movement.endPosition.clone();
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

handler.setInputAction(() => {
    isDragging = false;
    lastPosition = null;
}, Cesium.ScreenSpaceEventType.LEFT_UP);

14.5 本章小结

本章介绍了交互与事件处理:

  1. 事件处理器:ScreenSpaceEventHandler、事件类型
  2. 对象拾取:pick、drillPick、pickPosition
  3. 高亮选中:高亮管理器
  4. 绘图工具:点、线、面绘制
  5. 相机交互:自定义交互控制

在下一章中,我们将详细介绍样式与可视化效果。

14.6 思考与练习

  1. 实现完整的绘图工具栏。
  2. 开发实体拖拽移动功能。
  3. 实现框选多个对象功能。
  4. 开发右键菜单功能。
  5. 实现快捷键控制功能。
posted @ 2026-01-08 11:13  我才是银古  阅读(2)  评论(0)    收藏  举报