Cesium实体交互与事件处理

发布于:2025-05-09 ⋅ 阅读:(15) ⋅ 点赞:(0)

Cesium实体交互与事件处理完全指南

📊 Entity交互事件系统流程图

触发
处理
LEFT_CLICK
MOUSE_MOVE
LEFT_DOWN+MOUSE_MOVE
识别
信息展示
状态改变
位置修改
自定义行为
用户交互事件
ScreenSpaceEventHandler
事件类型
点选实体
悬停识别
拖拽操作
Scene.pick
获取Entity
处理类型
显示Entity属性
修改Entity外观
更新Entity位置
执行回调函数

🛠️ 事件处理基础

1. 创建事件处理器

// ⭐创建事件处理器 - 所有交互的入口
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

2. 常用事件类型

// 鼠标左键单击
Cesium.ScreenSpaceEventType.LEFT_CLICK
// 鼠标右键单击
Cesium.ScreenSpaceEventType.RIGHT_CLICK
// 鼠标移动
Cesium.ScreenSpaceEventType.MOUSE_MOVE
// 鼠标左键按下
Cesium.ScreenSpaceEventType.LEFT_DOWN
// 鼠标左键释放
Cesium.ScreenSpaceEventType.LEFT_UP
// 鼠标右键按下
Cesium.ScreenSpaceEventType.RIGHT_DOWN
// 鼠标右键释放
Cesium.ScreenSpaceEventType.RIGHT_UP
// 鼠标中键点击
Cesium.ScreenSpaceEventType.MIDDLE_CLICK
// 鼠标滚轮
Cesium.ScreenSpaceEventType.WHEEL
// 双击
Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK

🔍 实体拾取与选择

1. 基本实体拾取

// ⭐左键点击选择实体
handler.setInputAction(function(click) {
    // 获取点击位置的屏幕坐标
    const pickedObject = viewer.scene.pick(click.position);
    
    // 检查是否拾取到实体
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        // 获取拾取到的实体
        const entity = pickedObject.id;
        
        // 在控制台输出实体ID
        console.log('选中实体:', entity.id);
        
        // 处理选中逻辑
        highlightEntity(entity);
        showEntityInfo(entity);
    } else {
        // 点击空白区域
        clearSelection();
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 高亮显示实体
function highlightEntity(entity) {
    // 重置之前选中的实体
    clearSelection();
    
    // 记录当前选中的实体
    selectedEntity = entity;
    
    // 保存原始外观
    if (!entity._originalColor && entity.billboard) {
        // 对于广告牌类型,改变颜色或缩放
        entity._originalScale = entity.billboard.scale.getValue();
        entity.billboard.scale = entity._originalScale * 1.5;
    } else if (!entity._originalColor && entity.point) {
        // 对于点类型,改变颜色
        entity._originalColor = entity.point.color.getValue().clone();
        entity.point.color = Cesium.Color.YELLOW;
    } else if (!entity._originalColor && entity.polygon) {
        // 对于多边形类型,改变填充颜色
        entity._originalColor = entity.polygon.material.getValue().color.clone();
        entity.polygon.material = Cesium.Color.fromAlpha(Cesium.Color.YELLOW, 0.5);
        // 添加或强调轮廓
        entity.polygon.outline = true;
        entity.polygon.outlineColor = Cesium.Color.WHITE;
        entity.polygon.outlineWidth = 2.0;
    }
    
    // 聚焦到选中的实体
    viewer.flyTo(entity);
}

// 清除选择
function clearSelection() {
    if (selectedEntity) {
        // 恢复原始外观
        if (selectedEntity.billboard && selectedEntity._originalScale) {
            selectedEntity.billboard.scale = selectedEntity._originalScale;
            selectedEntity._originalScale = undefined;
        } else if (selectedEntity.point && selectedEntity._originalColor) {
            selectedEntity.point.color = selectedEntity._originalColor;
            selectedEntity._originalColor = undefined;
        } else if (selectedEntity.polygon && selectedEntity._originalColor) {
            selectedEntity.polygon.material = selectedEntity._originalColor;
            selectedEntity._originalColor = undefined;
        }
        
        selectedEntity = undefined;
        // 清除信息面板
        hideEntityInfo();
    }
}

// 显示实体信息
function showEntityInfo(entity) {
    // 创建或更新信息面板
    let infoBox = document.getElementById('entity-info-box');
    if (!infoBox) {
        infoBox = document.createElement('div');
        infoBox.id = 'entity-info-box';
        infoBox.style.position = 'absolute';
        infoBox.style.bottom = '10px';
        infoBox.style.right = '10px';
        infoBox.style.backgroundColor = 'rgba(40, 40, 40, 0.9)';
        infoBox.style.color = 'white';
        infoBox.style.padding = '10px';
        infoBox.style.borderRadius = '5px';
        infoBox.style.maxWidth = '300px';
        document.body.appendChild(infoBox);
    }
    
    // 构建信息内容
    let content = `<h3>${entity.name || entity.id || '未命名实体'}</h3>`;
    
    // 添加实体属性
    if (entity.properties) {
        content += '<table>';
        const propertyNames = entity.properties.propertyNames;
        for (let i = 0; i < propertyNames.length; i++) {
            const name = propertyNames[i];
            const value = entity.properties[name].getValue();
            content += `<tr><td>${name}</td><td>${value}</td></tr>`;
        }
        content += '</table>';
    } else {
        content += '<p>没有额外属性</p>';
    }
    
    // 获取位置信息
    if (entity.position) {
        const position = entity.position.getValue(viewer.clock.currentTime);
        const cartographic = Cesium.Cartographic.fromCartesian(position);
        const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
        const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
        const height = cartographic.height.toFixed(2);
        
        content += `<p>位置: ${longitude}, ${latitude}, ${height}m</p>`;
    }
    
    infoBox.innerHTML = content;
    infoBox.style.display = 'block';
}

// 隐藏实体信息
function hideEntityInfo() {
    const infoBox = document.getElementById('entity-info-box');
    if (infoBox) {
        infoBox.style.display = 'none';
    }
}

2. 高级拾取 - 拾取射线

// ⭐使用拾取射线进行精确拾取
handler.setInputAction(function(click) {
    // 从屏幕坐标创建射线
    const ray = viewer.camera.getPickRay(click.position);
    
    // 射线与地球求交得到交点
    const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
    
    // 拾取实体
    const pickedObject = viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        // 处理实体拾取
        console.log('拾取到实体:', pickedObject.id.id);
    } else if (Cesium.defined(cartesian)) {
        // 拾取到地球表面点
        const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
        const longitude = Cesium.Math.toDegrees(cartographic.longitude);
        const latitude = Cesium.Math.toDegrees(cartographic.latitude);
        const height = cartographic.height;
        
        console.log(`点击位置: 经度=${longitude.toFixed(6)}, 纬度=${latitude.toFixed(6)}, 高度=${height.toFixed(2)}`);
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

3. 深度检测拾取

// ⭐启用深度检测拾取 - 可穿透半透明实体
handler.setInputAction(function(click) {
    // 拾取所有与射线相交的对象
    const pickResult = viewer.scene.drillPick(click.position);
    
    if (pickResult.length > 0) {
        console.log(`拾取到${pickResult.length}个对象:`);
        
        // 遍历所有拾取到的对象
        for (let i = 0; i < pickResult.length; i++) {
            const pickedObject = pickResult[i];
            if (Cesium.defined(pickedObject.id)) {
                // 处理实体
                console.log(`- 实体 ${i+1}: ${pickedObject.id.id}`);
            } else if (pickedObject.primitive) {
                // 处理图元
                console.log(`- 图元 ${i+1}: ${pickedObject.primitive.constructor.name}`);
            }
        }
    } else {
        console.log('未拾取到任何对象');
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

4. 多选实体

// ⭐实现多选实体
const selectedEntities = new Set();

// 添加单击事件处理
handler.setInputAction(function(click) {
    const pickedObject = viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        const entity = pickedObject.id;
        
        // Ctrl键多选
        if (Cesium.KeyboardEventModifier.CTRL) {
            // 切换选择状态
            if (selectedEntities.has(entity)) {
                // 取消选择
                selectedEntities.delete(entity);
                resetEntityAppearance(entity);
            } else {
                // 添加到选择
                selectedEntities.add(entity);
                highlightEntity(entity);
            }
        } else {
            // 单选(先清除已选)
            clearMultiSelection();
            selectedEntities.add(entity);
            highlightEntity(entity);
        }
        
        updateSelectionInfo();
    } else if (!Cesium.KeyboardEventModifier.CTRL) {
        // 点击空白区域,取消所有选择
        clearMultiSelection();
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// 高亮显示实体
function highlightEntity(entity) {
    // 保存原始外观
    saveOriginalAppearance(entity);
    
    // 应用高亮样式
    if (entity.billboard) {
        entity.billboard.scale = entity._originalScale * 1.5;
    } else if (entity.point) {
        entity.point.color = Cesium.Color.YELLOW;
    } else if (entity.polygon) {
        entity.polygon.material = Cesium.Color.YELLOW.withAlpha(0.5);
        entity.polygon.outline = true;
        entity.polygon.outlineColor = Cesium.Color.WHITE;
    }
}

// 保存实体原始外观
function saveOriginalAppearance(entity) {
    if (!entity._originalAppearanceSaved) {
        if (entity.billboard) {
            entity._originalScale = entity.billboard.scale.getValue();
        } else if (entity.point) {
            entity._originalColor = entity.point.color.getValue().clone();
        } else if (entity.polygon && entity.polygon.material) {
            if (entity.polygon.material instanceof Cesium.ColorMaterialProperty) {
                entity._originalColor = entity.polygon.material.color.getValue().clone();
            } else {
                // 对于复杂材质,存储类型信息
                entity._originalMaterial = entity.polygon.material;
            }
        }
        
        entity._originalAppearanceSaved = true;
    }
}

// 重置实体外观
function resetEntityAppearance(entity) {
    if (entity._originalAppearanceSaved) {
        if (entity.billboard && entity._originalScale) {
            entity.billboard.scale = entity._originalScale;
        } else if (entity.point && entity._originalColor) {
            entity.point.color = entity._originalColor;
        } else if (entity.polygon) {
            if (entity._originalColor) {
                entity.polygon.material = entity._originalColor;
            } else if (entity._originalMaterial) {
                entity.polygon.material = entity._originalMaterial;
            }
        }
        
        entity._originalAppearanceSaved = false;
    }
}

// 清除多选
function clearMultiSelection() {
    selectedEntities.forEach(entity => {
        resetEntityAppearance(entity);
    });
    
    selectedEntities.clear();
    updateSelectionInfo();
}

// 更新选择信息
function updateSelectionInfo() {
    const selectionCount = selectedEntities.size;
    
    let selectionBox = document.getElementById('selection-info-box');
    if (!selectionBox) {
        selectionBox = document.createElement('div');
        selectionBox.id = 'selection-info-box';
        selectionBox.style.position = 'absolute';
        selectionBox.style.top = '10px';
        selectionBox.style.left = '10px';
        selectionBox.style.backgroundColor = 'rgba(40, 40, 40, 0.9)';
        selectionBox.style.color = 'white';
        selectionBox.style.padding = '10px';
        selectionBox.style.borderRadius = '5px';
        document.body.appendChild(selectionBox);
    }
    
    if (selectionCount > 0) {
        selectionBox.innerHTML = `已选择 ${selectionCount} 个实体`;
        selectionBox.style.display = 'block';
    } else {
        selectionBox.style.display = 'none';
    }
}

🖱️ 鼠标悬停交互

1. 悬停高亮效果

// ⭐实现鼠标悬停高亮
let hoveredEntity;

// 添加鼠标移动事件处理
handler.setInputAction(function(movement) {
    // 确保不干扰已选中实体
    if (selectedEntity) return;
    
    // 获取当前鼠标下的实体
    const pickedObject = viewer.scene.pick(movement.endPosition);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        const newHoveredEntity = pickedObject.id;
        
        // 避免重复处理同一实体
        if (hoveredEntity === newHoveredEntity) {
            return;
        }
        
        // 清除之前的悬停实体
        resetHoverEffect();
        
        // 设置新的悬停实体
        hoveredEntity = newHoveredEntity;
        
        // 应用悬停效果
        applyHoverEffect(hoveredEntity);
    } else {
        // 鼠标移开,清除悬停效果
        resetHoverEffect();
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// 应用悬停效果
function applyHoverEffect(entity) {
    if (entity.billboard) {
        entity._hoverOriginal = entity.billboard.scale.getValue();
        entity.billboard.scale = entity._hoverOriginal * 1.2;
    } else if (entity.point) {
        entity._hoverOriginal = entity.point.color.getValue().clone();
        entity.point.color = Cesium.Color.DEEPSKYBLUE;
    } else if (entity.polygon) {
        if (entity.polygon.material instanceof Cesium.ColorMaterialProperty) {
            entity._hoverOriginal = entity.polygon.material.color.getValue().clone();
            entity.polygon.material = new Cesium.ColorMaterialProperty(
                Cesium.Color.LIGHTSTEELBLUE.withAlpha(0.7)
            );
        }
    }
    
    // 修改光标样式
    viewer.canvas.style.cursor = 'pointer';
}

// 重置悬停效果
function resetHoverEffect() {
    if (hoveredEntity) {
        if (hoveredEntity.billboard && hoveredEntity._hoverOriginal) {
            hoveredEntity.billboard.scale = hoveredEntity._hoverOriginal;
            hoveredEntity._hoverOriginal = undefined;
        } else if (hoveredEntity.point && hoveredEntity._hoverOriginal) {
            hoveredEntity.point.color = hoveredEntity._hoverOriginal;
            hoveredEntity._hoverOriginal = undefined;
        } else if (hoveredEntity.polygon && hoveredEntity._hoverOriginal) {
            hoveredEntity.polygon.material = hoveredEntity._hoverOriginal;
            hoveredEntity._hoverOriginal = undefined;
        }
        
        hoveredEntity = undefined;
    }
    
    // 恢复默认光标
    viewer.canvas.style.cursor = 'default';
}

2. 悬停信息提示

// ⭐创建悬停信息提示
function createTooltip() {
    const tooltip = document.createElement('div');
    tooltip.className = 'cesium-tooltip';
    tooltip.style.display = 'none';
    tooltip.style.position = 'absolute';
    tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    tooltip.style.color = 'white';
    tooltip.style.padding = '6px 10px';
    tooltip.style.borderRadius = '4px';
    tooltip.style.pointerEvents = 'none';
    tooltip.style.zIndex = '1000';
    document.body.appendChild(tooltip);
    return tooltip;
}

// 创建提示框
const tooltip = createTooltip();

// 悬停显示提示
handler.setInputAction(function(movement) {
    const pickedObject = viewer.scene.pick(movement.endPosition);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        const entity = pickedObject.id;
        
        // 显示提示框
        tooltip.style.display = 'block';
        tooltip.style.left = movement.endPosition.x + 10 + 'px';
        tooltip.style.top = movement.endPosition.y + 10 + 'px';
        
        // 构建提示内容
        let content = entity.name || entity.id || '未命名实体';
        
        // 添加简短属性信息
        if (entity.properties) {
            const propertyNames = entity.properties.propertyNames;
            const maxProps = Math.min(3, propertyNames.length);
            
            if (maxProps > 0) {
                content += '<table style="margin-top:5px;">';
                for (let i = 0; i < maxProps; i++) {
                    const name = propertyNames[i];
                    const value = entity.properties[name].getValue();
                    content += `<tr><td>${name}</td><td>${value}</td></tr>`;
                }
                content += '</table>';
            }
        }
        
        tooltip.innerHTML = content;
    } else {
        // 隐藏提示框
        tooltip.style.display = 'none';
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

🔄 拖拽与位置编辑

1. 基本拖拽功能

// ⭐实现实体拖拽功能
let isDragging = false;
let draggedEntity;

// 鼠标按下事件 - 开始拖拽
handler.setInputAction(function(click) {
    // 如果已经在拖拽,不处理
    if (isDragging) return;
    
    // 拾取实体
    const pickedObject = viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        draggedEntity = pickedObject.id;
        
        // 标记为拖拽状态
        isDragging = true;
        
        // 修改光标样式
        viewer.canvas.style.cursor = 'grabbing';
        
        // 禁用相机旋转 - 防止拖拽时相机移动
        viewer.scene.screenSpaceCameraController.enableRotate = false;
    }
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

// 鼠标移动事件 - 拖拽中
handler.setInputAction(function(movement) {
    if (!isDragging || !draggedEntity) return;
    
    // 从屏幕坐标创建射线
    const ray = viewer.camera.getPickRay(movement.endPosition);
    
    // 射线与地球表面相交
    const newPosition = viewer.scene.globe.pick(ray, viewer.scene);
    
    if (Cesium.defined(newPosition)) {
        // 更新实体位置
        draggedEntity.position = newPosition;
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// 鼠标释放事件 - 结束拖拽
handler.setInputAction(function() {
    if (isDragging) {
        // 恢复状态
        isDragging = false;
        draggedEntity = undefined;
        
        // 恢复光标样式
        viewer.canvas.style.cursor = 'default';
        
        // 重新启用相机旋转
        viewer.scene.screenSpaceCameraController.enableRotate = true;
    }
}, Cesium.ScreenSpaceEventType.LEFT_UP);

2. 高级可编辑点位

// ⭐创建可编辑点位
function createEditablePoint(position, options = {}) {
    // 创建点实体
    const point = viewer.entities.add({
        position: position,
        point: {
            pixelSize: options.pixelSize || 10,
            color: options.color || Cesium.Color.RED,
            outlineColor: Cesium.Color.WHITE,
            outlineWidth: 2,
            heightReference: options.heightReference || Cesium.HeightReference.CLAMP_TO_GROUND
        },
        // 自定义属性
        properties: {
            isEditable: true
        }
    });
    
    return point;
}

// 创建可编辑多边形
function createEditablePolygon(positions, options = {}) {
    // 创建控制点
    const controlPoints = [];
    for (let i = 0; i < positions.length; i++) {
        const controlPoint = createEditablePoint(positions[i], {
            pixelSize: 8,
            color: Cesium.Color.LIME,
            heightReference: options.heightReference
        });
        
        // 添加索引属性
        controlPoint.properties.pointIndex = i;
        controlPoint.properties.polygonPoints = positions;
        
        controlPoints.push(controlPoint);
    }
    
    // 创建多边形
    const polygon = viewer.entities.add({
        polygon: {
            hierarchy: new Cesium.CallbackProperty(function() {
                // 动态获取控制点位置
                return new Cesium.PolygonHierarchy(positions);
            }, false),
            material: options.material || Cesium.Color.BLUE.withAlpha(0.5),
            heightReference: options.heightReference
        },
        properties: {
            controlPoints: controlPoints
        }
    });
    
    return {
        polygon: polygon,
        controlPoints: controlPoints
    };
}

// 实现点位拖拽
let isDragging = false;
let draggedPoint;

// 鼠标按下事件
handler.setInputAction(function(click) {
    if (isDragging) return;
    
    const pickedObject = viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        const entity = pickedObject.id;
        
        // 检查是否为可编辑点
        if (entity.properties && 
            entity.properties.isEditable && 
            entity.properties.isEditable.getValue()) {
            
            // 开始拖拽
            isDragging = true;
            draggedPoint = entity;
            
            // 修改光标样式
            viewer.canvas.style.cursor = 'grabbing';
            viewer.scene.screenSpaceCameraController.enableRotate = false;
        }
    }
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

// 鼠标移动事件
handler.setInputAction(function(movement) {
    if (!isDragging || !draggedPoint) return;
    
    // 获取新位置
    const ray = viewer.camera.getPickRay(movement.endPosition);
    const newPosition = viewer.scene.globe.pick(ray, viewer.scene);
    
    if (Cesium.defined(newPosition)) {
        // 更新拖拽点的位置
        draggedPoint.position = newPosition;
        
        // 如果是多边形控制点,更新对应多边形顶点
        if (draggedPoint.properties.polygonPoints && 
            draggedPoint.properties.pointIndex !== undefined) {
            
            const pointIndex = draggedPoint.properties.pointIndex.getValue();
            const polygonPoints = draggedPoint.properties.polygonPoints.getValue();
            
            // 更新多边形顶点位置
            polygonPoints[pointIndex] = newPosition;
        }
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// 鼠标释放事件
handler.setInputAction(function() {
    if (isDragging) {
        isDragging = false;
        draggedPoint = undefined;
        
        viewer.canvas.style.cursor = 'default';
        viewer.scene.screenSpaceCameraController.enableRotate = true;
    }
}, Cesium.ScreenSpaceEventType.LEFT_UP);

3. 可调整半径圆形

// ⭐创建可调整半径的圆
function createResizableCircle(center, radius, options = {}) {
    // 创建中心点
    const centerPoint = createEditablePoint(center, {
        pixelSize: 10,
        color: Cesium.Color.RED,
        heightReference: options.heightReference
    });
    
    // 创建初始半径
    const currentRadius = new Cesium.CallbackProperty(function() {
        return radius;
    }, false);
    
    // 创建边缘控制点位置 (初始在东侧)
    const edgePosition = new Cesium.CallbackProperty(function() {
        // 从中心点位置获取经纬度
        const centerCartographic = Cesium.Cartographic.fromCartesian(centerPoint.position.getValue());
        const lon = centerCartographic.longitude;
        const lat = centerCartographic.latitude;
        
        // 根据半径计算边缘点位置 (向东偏移)
        const radiusRadians = radius / 6378137.0; // 转换为弧度
        const edgeLon = lon + radiusRadians / Math.cos(lat);
        
        return Cesium.Cartesian3.fromRadians(edgeLon, lat, centerCartographic.height);
    }, false);
    
    // 创建边缘控制点
    const edgePoint = createEditablePoint(edgePosition, {
        pixelSize: 8,
        color: Cesium.Color.LIME,
        heightReference: options.heightReference
    });
    
    // 创建圆形
    const circle = viewer.entities.add({
        position: centerPoint.position,
        ellipse: {
            semiMajorAxis: currentRadius,
            semiMinorAxis: currentRadius,
            material: options.material || Cesium.Color.BLUE.withAlpha(0.5),
            heightReference: options.heightReference || Cesium.HeightReference.CLAMP_TO_GROUND,
            outline: true,
            outlineColor: Cesium.Color.WHITE
        },
        properties: {
            isResizableCircle: true,
            centerPoint: centerPoint,
            edgePoint: edgePoint
        }
    });
    
    // 特殊拖拽处理
    edgePoint.properties.isEdgePoint = true;
    edgePoint.properties.circleEntity = circle;
    centerPoint.properties.isCircleCenter = true;
    centerPoint.properties.circleEntity = circle;
    centerPoint.properties.edgePoint = edgePoint;
    
    return {
        circle: circle,
        centerPoint: centerPoint,
        edgePoint: edgePoint
    };
}

// 修改拖拽处理以支持圆形调整
handler.setInputAction(function(movement) {
    if (!isDragging || !draggedPoint) return;
    
    const ray = viewer.camera.getPickRay(movement.endPosition);
    const newPosition = viewer.scene.globe.pick(ray, viewer.scene);
    
    if (Cesium.defined(newPosition)) {
        if (draggedPoint.properties.isEdgePoint && 
            draggedPoint.properties.isEdgePoint.getValue()) {
            
            // 获取关联的圆
            const circle = draggedPoint.properties.circleEntity.getValue();
            const centerPoint = circle.properties.centerPoint.getValue();
            const centerPosition = centerPoint.position.getValue(viewer.clock.currentTime);
            
            // 更新控制点位置
            draggedPoint.position = newPosition;
            
            // 计算新半径
            radius = Cesium.Cartesian3.distance(centerPosition, newPosition);
            
            // 更新圆的半径
            circle.ellipse.semiMajorAxis = radius;
            circle.ellipse.semiMinorAxis = radius;
            
        } else if (draggedPoint.properties.isCircleCenter && 
                  draggedPoint.properties.isCircleCenter.getValue()) {
            
            // 移动中心点
            draggedPoint.position = newPosition;
            
            // 同时移动边缘点,保持相对位置
            const circle = draggedPoint.properties.circleEntity.getValue();
            const edgePoint = draggedPoint.properties.edgePoint.getValue();
            const radius = circle.ellipse.semiMajorAxis.getValue();
            
            // 计算新的边缘点位置
            const centerCartographic = Cesium.Cartographic.fromCartesian(newPosition);
            const edgeLon = centerCartographic.longitude + radius / 6378137.0 / Math.cos(centerCartographic.latitude);
            
            edgePoint.position = Cesium.Cartesian3.fromRadians(
                edgeLon,
                centerCartographic.latitude,
                centerCartographic.height
            );
        } else {
            // 普通点拖拽
            draggedPoint.position = newPosition;
        }
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

📏 测量与绘制工具

1. 距离测量工具

// ⭐实现距离测量工具
function createDistanceMeasurementTool() {
    // 保存状态和点
    let isActive = false;
    let isDrawing = false;
    let startPoint;
    let endPoint;
    let currentLine;
    let currentDistance;
    
    // 开始测量
    function start() {
        isActive = true;
        viewer.canvas.style.cursor = 'crosshair';
    }
    
    // 结束测量
    function stop() {
        isActive = false;
        isDrawing = false;
        viewer.canvas.style.cursor = 'default';
        
        startPoint = undefined;
        endPoint = undefined;
        currentLine = undefined;
        currentDistance = undefined;
    }
    
    // 计算两点间距离
    function calculateDistance(point1, point2) {
        return Cesium.Cartesian3.distance(point1, point2);
    }
    
    // 显示距离标签
    function createDistanceLabel(position, distance) {
        return viewer.entities.add({
            position: position,
            label: {
                text: `${distance.toFixed(2)}`,
                font: '14px sans-serif',
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                outlineWidth: 2,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                pixelOffset: new Cesium.Cartesian2(0, -10),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                disableDepthTestDistance: Number.POSITIVE_INFINITY
            }
        });
    }
    
    // 处理点击事件
    handler.setInputAction(function(click) {
        if (!isActive) return;
        
        // 从屏幕坐标获取地球表面点
        const ray = viewer.camera.getPickRay(click.position);
        const position = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (!Cesium.defined(position)) return;
        
        if (!isDrawing) {
            // 第一次点击,记录起点
            isDrawing = true;
            startPoint = position;
            
            // 创建起点标记
            viewer.entities.add({
                position: startPoint,
                point: {
                    pixelSize: 8,
                    color: Cesium.Color.RED,
                    outlineColor: Cesium.Color.WHITE,
                    outlineWidth: 2,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
                }
            });
            
            // 创建动态线
            currentLine = viewer.entities.add({
                polyline: {
                    positions: new Cesium.CallbackProperty(function() {
                        return [startPoint, endPoint || startPoint];
                    }, false),
                    width: 2,
                    material: Cesium.Color.YELLOW,
                    clampToGround: true
                }
            });
            
            // 创建动态距离标签
            currentDistance = viewer.entities.add({
                position: new Cesium.CallbackProperty(function() {
                    if (!endPoint) return startPoint;
                    
                    // 放在线的中间位置
                    return Cesium.Cartesian3.midpoint(startPoint, endPoint, new Cesium.Cartesian3());
                }, false),
                label: {
                    text: new Cesium.CallbackProperty(function() {
                        if (!endPoint) return "0.00 米";
                        
                        const distance = calculateDistance(startPoint, endPoint);
                        return `${distance.toFixed(2)}`;
                    }, false),
                    font: '14px sans-serif',
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    outlineWidth: 2,
                    backgroundColor: Cesium.Color.BLACK.withAlpha(0.7),
                    showBackground: true,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    pixelOffset: new Cesium.Cartesian2(0, -10),
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
                }
            });
        } else {
            // 第二次点击,完成测量
            endPoint = position;
            
            // 创建终点标记
            viewer.entities.add({
                position: endPoint,
                point: {
                    pixelSize: 8,
                    color: Cesium.Color.RED,
                    outlineColor: Cesium.Color.WHITE,
                    outlineWidth: 2,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
                }
            });
            
            // 计算最终距离
            const distance = calculateDistance(startPoint, endPoint);
            
            // 重置状态,准备下一次测量
            isDrawing = false;
            startPoint = undefined;
            endPoint = undefined;
            currentLine = undefined;
            currentDistance = undefined;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    
    // 鼠标移动事件处理
    handler.setInputAction(function(movement) {
        if (!isActive || !isDrawing) return;
        
        // 更新终点位置
        const ray = viewer.camera.getPickRay(movement.endPosition);
        endPoint = viewer.scene.globe.pick(ray, viewer.scene);
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    
    // 右键取消当前测量
    handler.setInputAction(function() {
        if (isDrawing) {
            // 删除临时实体
            if (currentLine) viewer.entities.remove(currentLine);
            if (currentDistance) viewer.entities.remove(currentDistance);
            
            // 重置状态
            isDrawing = false;
            startPoint = undefined;
            endPoint = undefined;
            currentLine = undefined;
            currentDistance = undefined;
        }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    
    return {
        start: start,
        stop: stop,
        isActive: function() { return isActive; }
    };
}

// 使用距离测量工具
const distanceTool = createDistanceMeasurementTool();

// 可以通过UI按钮触发测量
// 例如: document.getElementById('measureDistance').onclick = distanceTool.start;

2. 面积测量工具

// ⭐实现面积测量工具
function createAreaMeasurementTool() {
    // 保存状态和点
    let isActive = false;
    let points = [];
    let currentPolygon;
    let currentArea;
    
    // 开始测量
    function start() {
        isActive = true;
        points = [];
        viewer.canvas.style.cursor = 'crosshair';
    }
    
    // 结束测量
    function stop() {
        isActive = false;
        viewer.canvas.style.cursor = 'default';
        
        // 清除临时实体
        if (currentPolygon) viewer.entities.remove(currentPolygon);
        if (currentArea) viewer.entities.remove(currentArea);
        
        currentPolygon = undefined;
        currentArea = undefined;
        points = [];
    }
    
    // 计算多边形面积(平面近似)
    function calculateArea(positions) {
        if (positions.length < 3) return 0;
        
        // 将点转换为地理坐标
        const coordinates = [];
        for (let i = 0; i < positions.length; i++) {
            const cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
            coordinates.push([
                Cesium.Math.toDegrees(cartographic.longitude),
                Cesium.Math.toDegrees(cartographic.latitude)
            ]);
        }
        
        // 确保多边形闭合
        if (coordinates[0][0] !== coordinates[coordinates.length-1][0] ||
            coordinates[0][1] !== coordinates[coordinates.length-1][1]) {
            coordinates.push([coordinates[0][0], coordinates[0][1]]);
        }
        
        // 使用球面面积计算
        return Math.abs(calculateSphericalArea(coordinates));
    }
    
    // 球面多边形面积计算
    function calculateSphericalArea(coordinates) {
        const radiusEarth = 6378137.0; // 地球半径(米)
        let area = 0;
        
        if (coordinates.length > 2) {
            const len = coordinates.length;
            
            for (let i = 0; i < len - 1; i++) {
                const p1 = coordinates[i];
                const p2 = coordinates[i + 1];
                
                // 转换为弧度
                const p1Lon = Cesium.Math.toRadians(p1[0]);
                const p1Lat = Cesium.Math.toRadians(p1[1]);
                const p2Lon = Cesium.Math.toRadians(p2[0]);
                const p2Lat = Cesium.Math.toRadians(p2[1]);
                
                // 使用球面三角公式
                area += (p2Lon - p1Lon) * Math.sin(p1Lat);
            }
            
            area = area * radiusEarth * radiusEarth * 0.5;
        }
        
        return area;
    }
    
    // 格式化面积显示
    function formatArea(area) {
        if (area < 10000) {
            return `${area.toFixed(2)} 平方米`;
        } else {
            return `${(area / 1000000).toFixed(4)} 平方公里`;
        }
    }
    
    // 处理点击事件
    handler.setInputAction(function(click) {
        if (!isActive) return;
        
        // 从屏幕坐标获取地球表面点
        const ray = viewer.camera.getPickRay(click.position);
        const position = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (!Cesium.defined(position)) return;
        
        // 添加点
        points.push(position);
        
        // 创建点标记
        viewer.entities.add({
            position: position,
            point: {
                pixelSize: 8,
                color: Cesium.Color.LIME,
                outlineColor: Cesium.Color.WHITE,
                outlineWidth: 2,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
            }
        });
        
        // 至少有3个点时创建多边形
        if (points.length === 3) {
            // 创建多边形
            currentPolygon = viewer.entities.add({
                polygon: {
                    hierarchy: new Cesium.CallbackProperty(function() {
                        return new Cesium.PolygonHierarchy(points);
                    }, false),
                    material: Cesium.Color.BLUE.withAlpha(0.5),
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                    outline: true,
                    outlineColor: Cesium.Color.WHITE
                }
            });
            
            // 创建面积标签
            currentArea = viewer.entities.add({
                position: new Cesium.CallbackProperty(function() {
                    // 计算多边形中心
                    const center = Cesium.BoundingSphere.fromPoints(points).center;
                    return center;
                }, false),
                label: {
                    text: new Cesium.CallbackProperty(function() {
                        const area = calculateArea(points);
                        return `面积: ${formatArea(area)}`;
                    }, false),
                    font: '14px sans-serif',
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    outlineWidth: 2,
                    backgroundColor: Cesium.Color.BLACK.withAlpha(0.7),
                    showBackground: true,
                    verticalOrigin: Cesium.VerticalOrigin.CENTER,
                    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
                }
            });
        } else if (points.length > 3) {
            // 当添加更多点时,多边形会自动更新
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    
    // 双击完成绘制
    handler.setInputAction(function(click) {
        if (!isActive || points.length < 3) return;
        
        // 创建最终多边形和面积标签
        const finalPoints = [...points];
        
        // 计算面积
        const area = calculateArea(finalPoints);
        
        // 创建永久多边形
        viewer.entities.add({
            polygon: {
                hierarchy: new Cesium.PolygonHierarchy(finalPoints),
                material: Cesium.Color.GREEN.withAlpha(0.5),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                outline: true,
                outlineColor: Cesium.Color.WHITE
            }
        });
        
        // 创建永久面积标签
        viewer.entities.add({
            position: Cesium.BoundingSphere.fromPoints(finalPoints).center,
            label: {
                text: `面积: ${formatArea(area)}`,
                font: '14px sans-serif',
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                outlineWidth: 2,
                backgroundColor: Cesium.Color.BLACK.withAlpha(0.7),
                showBackground: true,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                disableDepthTestDistance: Number.POSITIVE_INFINITY
            }
        });
        
        // 重置当前工作状态
        points = [];
        if (currentPolygon) viewer.entities.remove(currentPolygon);
        if (currentArea) viewer.entities.remove(currentArea);
        currentPolygon = undefined;
        currentArea = undefined;
    }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    
    // 右键取消当前点
    handler.setInputAction(function() {
        if (!isActive || points.length === 0) return;
        
        // 移除最后一个点
        points.pop();
        
        // 如果不足3个点,移除多边形
        if (points.length < 3) {
            if (currentPolygon) viewer.entities.remove(currentPolygon);
            if (currentArea) viewer.entities.remove(currentArea);
            currentPolygon = undefined;
            currentArea = undefined;
        }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    
    return {
        start: start,
        stop: stop,
        isActive: function() { return isActive; }
    };
}

// 使用面积测量工具
const areaTool = createAreaMeasurementTool();

// 可以通过UI按钮触发测量
// 例如: document.getElementById('measureArea').onclick = areaTool.start;

🖌️ 自定义绘制工具

1. 绘制矩形工具

// ⭐实现矩形绘制工具
function createRectangleDrawingTool(options = {}) {
    // 保存状态
    let isActive = false;
    let isDrawing = false;
    let startPosition;
    let endPosition;
    let currentRectangle;
    
    // 开始绘制
    function start() {
        isActive = true;
        viewer.canvas.style.cursor = 'crosshair';
    }
    
    // 结束绘制
    function stop() {
        isActive = false;
        isDrawing = false;
        viewer.canvas.style.cursor = 'default';
        
        // 清除临时实体
        if (currentRectangle) viewer.entities.remove(currentRectangle);
        
        startPosition = undefined;
        endPosition = undefined;
        currentRectangle = undefined;
    }
    
    // 处理鼠标按下事件 - 开始绘制
    handler.setInputAction(function(click) {
        if (!isActive || isDrawing) return;
        
        // 获取起点位置
        const ray = viewer.camera.getPickRay(click.position);
        startPosition = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (!Cesium.defined(startPosition)) return;
        
        isDrawing = true;
        
        // 创建临时矩形
        currentRectangle = viewer.entities.add({
            rectangle: {
                coordinates: new Cesium.CallbackProperty(function() {
                    if (!startPosition || !endPosition) {
                        return Cesium.Rectangle.fromDegrees(0, 0, 0, 0);
                    }
                    
                    // 计算矩形坐标
                    const startCartographic = Cesium.Cartographic.fromCartesian(startPosition);
                    const endCartographic = Cesium.Cartographic.fromCartesian(endPosition);
                    
                    const west = Math.min(startCartographic.longitude, endCartographic.longitude);
                    const east = Math.max(startCartographic.longitude, endCartographic.longitude);
                    const south = Math.min(startCartographic.latitude, endCartographic.latitude);
                    const north = Math.max(startCartographic.latitude, endCartographic.latitude);
                    
                    return Cesium.Rectangle.fromRadians(west, south, east, north);
                }, false),
                material: options.material || Cesium.Color.BLUE.withAlpha(0.5),
                height: options.height || 0,
                heightReference: options.heightReference || Cesium.HeightReference.CLAMP_TO_GROUND,
                outline: true,
                outlineColor: Cesium.Color.WHITE
            }
        });
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    
    // 处理鼠标移动事件 - 更新矩形
    handler.setInputAction(function(movement) {
        if (!isActive || !isDrawing) return;
        
        // 更新终点位置
        const ray = viewer.camera.getPickRay(movement.endPosition);
        endPosition = viewer.scene.globe.pick(ray, viewer.scene);
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    
    // 处理鼠标释放事件 - 完成绘制
    handler.setInputAction(function() {
        if (!isActive || !isDrawing) return;
        
        isDrawing = false;
        
        // 创建永久矩形
        if (startPosition && endPosition) {
            // 计算最终矩形坐标
            const startCartographic = Cesium.Cartographic.fromCartesian(startPosition);
            const endCartographic = Cesium.Cartographic.fromCartesian(endPosition);
            
            const west = Math.min(startCartographic.longitude, endCartographic.longitude);
            const east = Math.max(startCartographic.longitude, endCartographic.longitude);
            const south = Math.min(startCartographic.latitude, endCartographic.latitude);
            const north = Math.max(startCartographic.latitude, endCartographic.latitude);
            
            // 创建最终矩形
            const finalRectangle = viewer.entities.add({
                rectangle: {
                    coordinates: Cesium.Rectangle.fromRadians(west, south, east, north),
                    material: options.material || Cesium.Color.BLUE.withAlpha(0.5),
                    height: options.height || 0,
                    heightReference: options.heightReference || Cesium.HeightReference.CLAMP_TO_GROUND,
                    outline: true,
                    outlineColor: Cesium.Color.WHITE
                }
            });
            
            // 如果提供了回调函数,则调用
            if (options.callback) {
                options.callback(finalRectangle);
            }
        }
        
        // 清除临时实体
        if (currentRectangle) viewer.entities.remove(currentRectangle);
        
        // 重置状态
        startPosition = undefined;
        endPosition = undefined;
        currentRectangle = undefined;
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
    
    // 右键取消当前绘制
    handler.setInputAction(function() {
        if (isDrawing) {
            isDrawing = false;
            
            // 清除临时实体
            if (currentRectangle) viewer.entities.remove(currentRectangle);
            
            // 重置状态
            startPosition = undefined;
            endPosition = undefined;
            currentRectangle = undefined;
        }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    
    return {
        start: start,
        stop: stop,
        isActive: function() { return isActive; }
    };
}

// 使用矩形绘制工具
const rectangleTool = createRectangleDrawingTool({
    material: Cesium.Color.RED.withAlpha(0.5),
    callback: function(entity) {
        console.log('矩形绘制完成:', entity);
    }
});

// 可以通过UI按钮触发绘制
// 例如: document.getElementById('drawRectangle').onclick = rectangleTool.start;

2. 自定义路径绘制工具

// ⭐实现路径绘制工具
function createPathDrawingTool(options = {}) {
    // 保存状态
    let isActive = false;
    let positions = [];
    let currentLine;
    
    // 开始绘制
    function start() {
        isActive = true;
        positions = [];
        viewer.canvas.style.cursor = 'crosshair';
    }
    
    // 结束绘制
    function stop() {
        isActive = false;
        viewer.canvas.style.cursor = 'default';
        
        // 清除临时实体
        if (currentLine) viewer.entities.remove(currentLine);
        
        currentLine = undefined;
        positions = [];
    }
    
    // 处理点击事件 - 添加路径点
    handler.setInputAction(function(click) {
        if (!isActive) return;
        
        // 获取点击位置
        const ray = viewer.camera.getPickRay(click.position);
        const position = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (!Cesium.defined(position)) return;
        
        // 添加点
        positions.push(position);
        
        // 创建点标记
        viewer.entities.add({
            position: position,
            point: {
                pixelSize: 8,
                color: Cesium.Color.YELLOW,
                outlineColor: Cesium.Color.WHITE,
                outlineWidth: 2,
                heightReference: options.heightReference || Cesium.HeightReference.CLAMP_TO_GROUND
            }
        });
        
        // 第一个点时创建线
        if (positions.length === 1) {
            currentLine = viewer.entities.add({
                polyline: {
                    positions: new Cesium.CallbackProperty(function() {
                        return positions;
                    }, false),
                    width: options.width || 3,
                    material: options.material || Cesium.Color.YELLOW,
                    clampToGround: options.clampToGround !== false
                }
            });
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    
    // 双击完成绘制
    handler.setInputAction(function() {
        if (!isActive || positions.length < 2) return;
        
        // 创建最终路径
        const finalPositions = [...positions];
        
        viewer.entities.add({
            polyline: {
                positions: finalPositions,
                width: options.width || 3,
                material: options.material || new Cesium.PolylineGlowMaterialProperty({
                    glowPower: 0.2,
                    color: Cesium.Color.YELLOW
                }),
                clampToGround: options.clampToGround !== false
            }
        });
        
        // 如果提供了回调函数,则调用
        if (options.callback) {
            options.callback(finalPositions);
        }
        
        // 重置状态
        positions = [];
        if (currentLine) viewer.entities.remove(currentLine);
        currentLine = undefined;
    }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    
    // 鼠标移动事件 - 预览路径
    handler.setInputAction(function(movement) {
        if (!isActive || positions.length === 0) return;
        
        // 获取当前鼠标位置
        const ray = viewer.camera.getPickRay(movement.endPosition);
        const currentPosition = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (!Cesium.defined(currentPosition)) return;
        
        // 更新预览路径
        if (!currentLine && positions.length > 0) {
            currentLine = viewer.entities.add({
                polyline: {
                    positions: new Cesium.CallbackProperty(function() {
                        return [...positions, currentPosition];
                    }, false),
                    width: options.width || 3,
                    material: options.material || Cesium.Color.YELLOW,
                    clampToGround: options.clampToGround !== false
                }
            });
        }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    
    // 右键删除最后一个点
    handler.setInputAction(function() {
        if (!isActive || positions.length === 0) return;
        
        // 移除最后一个点
        positions.pop();
        
        // 如果没有点了,删除线
        if (positions.length === 0 && currentLine) {
            viewer.entities.remove(currentLine);
            currentLine = undefined;
        }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    
    return {
        start: start,
        stop: stop,
        isActive: function() { return isActive; }
    };
}

// 使用路径绘制工具
const pathTool = createPathDrawingTool({
    material: new Cesium.PolylineGlowMaterialProperty({
        glowPower: 0.2,
        color: Cesium.Color.GREEN
    }),
    width: 5,
    callback: function(positions) {
        console.log('路径绘制完成,点数:', positions.length);
    }
});

// 可以通过UI按钮触发绘制
// 例如: document.getElementById('drawPath').onclick = pathTool.start;

📱 触摸设备交互

1. 适配触摸设备

// ⭐适配触摸设备的实体选择
function setupTouchInteraction() {
    // 创建触摸事件处理器
    const touchHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    
    // 触摸点击(单指点击)
    touchHandler.setInputAction(function(tap) {
        // 获取触摸位置
        const pickedObject = viewer.scene.pick(tap.position);
        
        if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
            const entity = pickedObject.id;
            
            // 处理实体选择
            console.log('触摸选择实体:', entity.id);
            
            // 高亮显示
            highlightEntity(entity);
            showEntityInfo(entity);
        } else {
            // 点击空白区域
            clearSelection();
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    
    // 处理长按事件(模拟右键)
    let touchStartPosition;
    let touchStartTime;
    const LONG_PRESS_THRESHOLD = 800; // 毫秒
    
    // 触摸开始
    touchHandler.setInputAction(function(touch) {
        touchStartPosition = touch.position.clone();
        touchStartTime = Date.now();
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    
    // 触摸结束
    touchHandler.setInputAction(function(touch) {
        // 检查是否是长按
        const touchEndTime = Date.now();
        const touchDuration = touchEndTime - touchStartTime;
        
        if (touchDuration >= LONG_PRESS_THRESHOLD) {
            // 长按处理 - 模拟右键菜单
            const pickedObject = viewer.scene.pick(touchStartPosition);
            
            if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
                const entity = pickedObject.id;
                showContextMenu(entity, touchStartPosition);
            }
        }
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
    
    // 显示上下文菜单
    function showContextMenu(entity, position) {
        // 创建或获取上下文菜单
        let contextMenu = document.getElementById('entity-context-menu');
        if (!contextMenu) {
            contextMenu = document.createElement('div');
            contextMenu.id = 'entity-context-menu';
            contextMenu.style.position = 'absolute';
            contextMenu.style.backgroundColor = 'rgba(40, 40, 40, 0.9)';
            contextMenu.style.color = 'white';
            contextMenu.style.padding = '10px';
            contextMenu.style.borderRadius = '5px';
            contextMenu.style.zIndex = '1000';
            document.body.appendChild(contextMenu);
        }
        
        // 设置菜单位置
        contextMenu.style.left = position.x + 'px';
        contextMenu.style.top = position.y + 'px';
        
        // 构建菜单内容
        contextMenu.innerHTML = `
            <div class="menu-item" data-action="zoom">放大到此实体</div>
            <div class="menu-item" data-action="hide">隐藏此实体</div>
            <div class="menu-item" data-action="info">显示详细信息</div>
        `;
        
        // 添加菜单项点击事件
        const menuItems = contextMenu.querySelectorAll('.menu-item');
        menuItems.forEach(item => {
            item.addEventListener('click', function() {
                const action = this.getAttribute('data-action');
                
                switch(action) {
                    case 'zoom':
                        viewer.flyTo(entity);
                        break;
                    case 'hide':
                        entity.show = false;
                        break;
                    case 'info':
                        showEntityInfo(entity);
                        break;
                }
                
                // 隐藏菜单
                contextMenu.style.display = 'none';
            });
        });
        
        // 显示菜单
        contextMenu.style.display = 'block';
        
        // 点击其他区域隐藏菜单
        const hideMenu = function() {
            contextMenu.style.display = 'none';
            document.removeEventListener('click', hideMenu);
        };
        
        setTimeout(() => {
            document.addEventListener('click', hideMenu);
        }, 100);
    }
    
    // 处理多点触控(缩放、平移)
    // 注意:Cesium已内置处理了大部分触控手势,此处仅作示例
}

// 初始化触摸交互
setupTouchInteraction();

🧩 高级交互案例

1. 实体联动与关联

// ⭐实现实体联动与关联
function createLinkedEntities() {
    // 创建主实体
    const mainEntity = viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(116.39, 39.9),
        billboard: {
            image: './images/marker-red.png',
            scale: 0.8,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM
        },
        label: {
            text: '主控点',
            font: '14px sans-serif',
            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
            outlineWidth: 2,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(0, -36)
        },
        // 自定义属性
        properties: {
            type: 'main',
            linkedEntities: []
        }
    });
    
    // 创建从属实体
    const childEntities = [];
    
    for (let i = 0; i < 5; i++) {
        // 创建子实体
        const childEntity = viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(
                116.39 + (Math.random() - 0.5) * 0.1,
                39.9 + (Math.random() - 0.5) * 0.1
            ),
            billboard: {
                image: './images/marker-blue.png',
                scale: 0.6,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM
            },
            label: {
                text: `子点 ${i+1}`,
                font: '12px sans-serif',
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                outlineWidth: 2,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                pixelOffset: new Cesium.Cartesian2(0, -30)
            },
            // 自定义属性
            properties: {
                type: 'child',
                parentEntity: mainEntity,
                index: i
            }
        });
        
        childEntities.push(childEntity);
        
        // 添加到主实体的关联列表
        mainEntity.properties.linkedEntities.push(childEntity);
        
        // 创建连接线
        viewer.entities.add({
            polyline: {
                positions: new Cesium.CallbackProperty(function() {
                    const mainPosition = mainEntity.position.getValue(viewer.clock.currentTime);
                    const childPosition = childEntity.position.getValue(viewer.clock.currentTime);
                    return [mainPosition, childPosition];
                }, false),
                width: 1,
                material: new Cesium.PolylineDashMaterialProperty({
                    color: Cesium.Color.CYAN
                })
            },
            properties: {
                type: 'connection',
                sourceEntity: mainEntity,
                targetEntity: childEntity
            }
        });
    }
    
    // 实现拖拽主实体时联动
    let isDragging = false;
    let draggedEntity;
    let startMousePosition;
    let startEntityPosition;
    
    // 鼠标按下事件
    handler.setInputAction(function(click) {
        if (isDragging) return;
        
        // 拾取实体
        const pickedObject = viewer.scene.pick(click.position);
        
        if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
            draggedEntity = pickedObject.id;
            
            // 只处理主实体
            if (draggedEntity.properties && 
                draggedEntity.properties.type && 
                draggedEntity.properties.type.getValue() === 'main') {
                
                // 标记为拖拽状态
                isDragging = true;
                
                // 保存初始位置
                startMousePosition = click.position.clone();
                startEntityPosition = draggedEntity.position.getValue(viewer.clock.currentTime).clone();
                
                // 修改光标样式
                viewer.canvas.style.cursor = 'grabbing';
                
                // 禁用相机旋转
                viewer.scene.screenSpaceCameraController.enableRotate = false;
            }
        }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    
    // 鼠标移动事件
    handler.setInputAction(function(movement) {
        if (!isDragging || !draggedEntity) return;
        
        // 计算鼠标位移
        const dx = movement.endPosition.x - startMousePosition.x;
        const dy = movement.endPosition.y - startMousePosition.y;
        
        // 将屏幕位移转换为地理位移
        const ray = viewer.camera.getPickRay(movement.endPosition);
        const newPosition = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (Cesium.defined(newPosition)) {
            // 更新主实体位置
            draggedEntity.position = newPosition;
            
            // 获取子实体列表
            const linkedEntities = draggedEntity.properties.linkedEntities.getValue();
            
            // 计算位移向量
            const oldCartographic = Cesium.Cartographic.fromCartesian(startEntityPosition);
            const newCartographic = Cesium.Cartographic.fromCartesian(newPosition);
            
            const lonOffset = newCartographic.longitude - oldCartographic.longitude;
            const latOffset = newCartographic.latitude - oldCartographic.latitude;
            
            // 更新所有关联实体位置
            for (let i = 0; i < linkedEntities.length; i++) {
                const childEntity = linkedEntities[i];
                const childPosition = childEntity.position.getValue(viewer.clock.currentTime);
                const childCartographic = Cesium.Cartographic.fromCartesian(childPosition);
                
                // 应用相同的偏移
                const newChildPosition = Cesium.Cartesian3.fromRadians(
                    childCartographic.longitude + lonOffset,
                    childCartographic.latitude + latOffset,
                    childCartographic.height
                );
                
                childEntity.position = newChildPosition;
            }
            
            // 更新起始位置,以防持续拖拽
            startMousePosition = movement.endPosition.clone();
            startEntityPosition = newPosition.clone();
        }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    
    // 鼠标释放事件
    handler.setInputAction(function() {
        if (isDragging) {
            // 恢复状态
            isDragging = false;
            draggedEntity = undefined;
            
            // 恢复光标样式
            viewer.canvas.style.cursor = 'default';
            
            // 重新启用相机旋转
            viewer.scene.screenSpaceCameraController.enableRotate = true;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
    
    return {
        mainEntity: mainEntity,
        childEntities: childEntities
    };
}

// 创建联动实体组
const linkedEntitiesGroup = createLinkedEntities();

2. 自定义交互控制器

// ⭐创建自定义交互控制器
function createEntityControlPanel(entity) {
    // 创建控制面板
    let controlPanel = document.getElementById('entity-control-panel');
    if (!controlPanel) {
        controlPanel = document.createElement('div');
        controlPanel.id = 'entity-control-panel';
        controlPanel.style.position = 'absolute';
        controlPanel.style.bottom = '20px';
        controlPanel.style.left = '20px';
        controlPanel.style.backgroundColor = 'rgba(40, 40, 40, 0.9)';
        controlPanel.style.color = 'white';
        controlPanel.style.padding = '10px';
        controlPanel.style.borderRadius = '5px';
        controlPanel.style.maxWidth = '300px';
        controlPanel.style.display = 'none';
        document.body.appendChild(controlPanel);
    }
    
    // 更新控制面板内容
    controlPanel.innerHTML = `
        <h3>${entity.name || entity.id || '未命名实体'}</h3>
        <div>
            <label>可见性:</label>
            <input type="checkbox" id="entity-visibility" ${entity.show ? 'checked' : ''}>
        </div>
        <div>
            <label>透明度:</label>
            <input type="range" id="entity-opacity" min="0" max="1" step="0.1" value="1">
        </div>
        <div>
            <label>高度:</label>
            <input type="range" id="entity-height" min="0" max="1000" step="10" value="0">
        </div>
        <div class="button-group">
            <button id="entity-zoom">缩放至</button>
            <button id="entity-delete">删除</button>
        </div>
    `;
    
    // 显示控制面板
    controlPanel.style.display = 'block';
    
    // 获取控制元素
    const visibilityCheckbox = document.getElementById('entity-visibility');
    const opacitySlider = document.getElementById('entity-opacity');
    const heightSlider = document.getElementById('entity-height');
    const zoomButton = document.getElementById('entity-zoom');
    const deleteButton = document.getElementById('entity-delete');
    
    // 设置初始值
    if (entity.position) {
        const position = entity.position.getValue(viewer.clock.currentTime);
        const cartographic = Cesium.Cartographic.fromCartesian(position);
        heightSlider.value = cartographic.height;
    }
    
    // 处理可见性变化
    visibilityCheckbox.addEventListener('change', function() {
        entity.show = this.checked;
    });
    
    // 处理透明度变化
    opacitySlider.addEventListener('input', function() {
        const opacity = parseFloat(this.value);
        
        // 根据实体类型应用透明度
        if (entity.billboard) {
            entity.billboard.color = Cesium.Color.WHITE.withAlpha(opacity);
        } else if (entity.polygon && entity.polygon.material) {
            // 对于材质,我们需要保存原始颜色
            if (!entity._originalMaterialColor) {
                if (entity.polygon.material instanceof Cesium.ColorMaterialProperty) {
                    entity._originalMaterialColor = entity.polygon.material.color.getValue().clone();
                } else {
                    entity._originalMaterialColor = Cesium.Color.BLUE;
                }
            }
            
            entity.polygon.material = entity._originalMaterialColor.withAlpha(opacity);
        } else if (entity.polyline && entity.polyline.material) {
            if (!entity._originalLineColor) {
                if (entity.polyline.material instanceof Cesium.ColorMaterialProperty) {
                    entity._originalLineColor = entity.polyline.material.color.getValue().clone();
                } else {
                    entity._originalLineColor = Cesium.Color.WHITE;
                }
            }
            
            entity.polyline.material = entity._originalLineColor.withAlpha(opacity);
        }
    });
    
    // 处理高度变化
    heightSlider.addEventListener('input', function() {
        const height = parseFloat(this.value);
        
        if (entity.position) {
            // 获取当前位置
            const position = entity.position.getValue(viewer.clock.currentTime);
            const cartographic = Cesium.Cartographic.fromCartesian(position);
            
            // 更新高度
            const newPosition = Cesium.Cartesian3.fromRadians(
                cartographic.longitude,
                cartographic.latitude,
                height
            );
            
            entity.position = newPosition;
        }
    });
    
    // 缩放到实体
    zoomButton.addEventListener('click', function() {
        viewer.flyTo(entity);
    });
    
    // 删除实体
    deleteButton.addEventListener('click', function() {
        viewer.entities.remove(entity);
        controlPanel.style.display = 'none';
    });
    
    return controlPanel;
}

// 使用控制面板
handler.setInputAction(function(click) {
    const pickedObject = viewer.scene.pick(click.position);
    
    if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
        const entity = pickedObject.id;
        createEntityControlPanel(entity);
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

3. 实体状态切换与动画

// ⭐实现实体状态切换与动画
function createAnimatedEntity(position, options = {}) {
    // 状态定义
    const states = {
        normal: {
            color: Cesium.Color.GREEN,
            scale: 1.0,
            pulseRate: 0.0
        },
        warning: {
            color: Cesium.Color.YELLOW,
            scale: 1.2,
            pulseRate: 0.5
        },
        alert: {
            color: Cesium.Color.RED,
            scale: 1.5,
            pulseRate: 1.0
        }
    };
    
    // 当前状态
    let currentState = 'normal';
    
    // 创建状态指示器实体
    const entity = viewer.entities.add({
        position: position,
        // 点
        point: {
            pixelSize: 20,
            color: new Cesium.CallbackProperty(function(time) {
                const state = states[currentState];
                
                // 如果有脉冲效果
                if (state.pulseRate > 0) {
                    const seconds = Cesium.JulianDate.secondsDifference(time, viewer.clock.startTime);
                    const pulseFactor = 0.5 + 0.5 * Math.sin(seconds * state.pulseRate * Math.PI);
                    
                    // 在原始颜色和白色之间插值
                    return Cesium.Color.lerp(
                        state.color,
                        Cesium.Color.WHITE,
                        pulseFactor * 0.5,
                        new Cesium.Color()
                    );
                }
                
                return state.color;
            }, false),
            outlineColor: Cesium.Color.WHITE,
            outlineWidth: 2
        },
        // 圆形
        ellipse: {
            semiMajorAxis: 100,
            semiMinorAxis: 100,
            material: new Cesium.CallbackProperty(function(time) {
                const state = states[currentState];
                
                // 如果有脉冲效果
                if (state.pulseRate > 0) {
                    const seconds = Cesium.JulianDate.secondsDifference(time, viewer.clock.startTime);
                    const pulseFactor = 0.5 + 0.5 * Math.sin(seconds * state.pulseRate * Math.PI);
                    
                    // 动态创建材质
                    return new Cesium.ColorMaterialProperty(
                        Cesium.Color.fromAlpha(state.color, 0.3 + 0.2 * pulseFactor)
                    );
                }
                
                return new Cesium.ColorMaterialProperty(
                    Cesium.Color.fromAlpha(state.color, 0.3)
                );
            }, false),
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            outline: true,
            outlineColor: new Cesium.CallbackProperty(function() {
                return states[currentState].color;
            }, false)
        },
        // 标签
        label: {
            text: options.text || '状态指示器',
            font: '14px sans-serif',
            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
            outlineWidth: 2,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(0, -30),
            backgroundColor: new Cesium.CallbackProperty(function() {
                return Cesium.Color.fromAlpha(states[currentState].color, 0.7);
            }, false),
            showBackground: true,
            scale: new Cesium.CallbackProperty(function(time) {
                const state = states[currentState];
                
                // 如果有脉冲效果
                if (state.pulseRate > 0) {
                    const seconds = Cesium.JulianDate.secondsDifference(time, viewer.clock.startTime);
                    const pulseFactor = 0.5 + 0.5 * Math.sin(seconds * state.pulseRate * Math.PI);
                    
                    return state.scale * (1.0 + pulseFactor * 0.1);
                }
                
                return state.scale;
            }, false)
        },
        // 自定义属性
        properties: {
            state: currentState
        }
    });
    
    // 切换状态方法
    function setState(stateName) {
        if (states[stateName]) {
            currentState = stateName;
            entity.properties.state = currentState;
        }
    }
    
    // 添加点击事件处理
    handler.setInputAction(function(click) {
        const pickedObject = viewer.scene.pick(click.position);
        
        if (Cesium.defined(pickedObject) && pickedObject.id === entity) {
            // 循环切换状态
            const stateNames = Object.keys(states);
            const currentIndex = stateNames.indexOf(currentState);
            const nextIndex = (currentIndex + 1) % stateNames.length;
            
            setState(stateNames[nextIndex]);
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    
    return {
        entity: entity,
        setState: setState
    };
}

// 创建动态状态指示器
const stateIndicator = createAnimatedEntity(
    Cesium.Cartesian3.fromDegrees(116.4, 39.9),
    { text: '状态指示器' }
);

// 状态自动切换示例
let stateIndex = 0;
const states = ['normal', 'warning', 'alert'];

// 每5秒切换一次状态
setInterval(() => {
    stateIndex = (stateIndex + 1) % states.length;
    stateIndicator.setState(states[stateIndex]);
}, 5000);

4. 高级拖放实体交互

// ⭐实现拖放交互 - 支持在目标区域放置拖动的实体
function setupDragAndDropInteraction() {
    // 创建一些可拖动实体
    const draggableEntities = [];
    
    // 生成不同类型的可拖动实体
    function createDraggableEntity(position, options = {}) {
        const entity = viewer.entities.add({
            position: position,
            billboard: {
                image: options.image || './images/draggable.png',
                scale: 0.8,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM
            },
            label: {
                text: options.text || '可拖动',
                font: '12px sans-serif',
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                outlineWidth: 2,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                pixelOffset: new Cesium.Cartesian2(0, -30)
            },
            properties: {
                isDraggable: true,
                type: options.type || 'default',
                value: options.value || 0
            }
        });
        
        draggableEntities.push(entity);
        return entity;
    }
    
    // 创建目标区域
    function createDropTarget(position, options = {}) {
        const radius = options.radius || 200;
        
        const entity = viewer.entities.add({
            position: position,
            ellipse: {
                semiMajorAxis: radius,
                semiMinorAxis: radius,
                material: Cesium.Color.fromAlpha(options.color || Cesium.Color.BLUE, 0.3),
                outline: true,
                outlineColor: options.color || Cesium.Color.BLUE,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
            },
            label: {
                text: options.text || '目标区域',
                font: '16px sans-serif',
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                outlineWidth: 2,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
            },
            properties: {
                isDropTarget: true,
                acceptTypes: options.acceptTypes || ['default'],
                radius: radius,
                onDrop: options.onDrop
            }
        });
        
        return entity;
    }
    
    // 拖拽状态
    let isDragging = false;
    let draggedEntity;
    let originalPosition;
    let hoveredDropTarget;
    
    // 鼠标按下开始拖拽
    handler.setInputAction(function(click) {
        if (isDragging) return;
        
        const pickedObject = viewer.scene.pick(click.position);
        
        if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
            const entity = pickedObject.id;
            
            // 检查是否为可拖动实体
            if (entity.properties && entity.properties.isDraggable && 
                entity.properties.isDraggable.getValue()) {
                
                // 开始拖拽
                isDragging = true;
                draggedEntity = entity;
                
                // 保存原始位置(用于取消拖拽)
                originalPosition = entity.position.getValue(viewer.clock.currentTime).clone();
                
                // 修改视觉样式表示拖拽状态
                if (entity.billboard) {
                    entity._originalScale = entity.billboard.scale.getValue();
                    entity.billboard.scale = entity._originalScale * 1.3;
                }
                
                // 修改光标样式
                viewer.canvas.style.cursor = 'grabbing';
                
                // 禁用相机旋转
                viewer.scene.screenSpaceCameraController.enableRotate = false;
            }
        }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    
    // 鼠标移动处理拖拽
    handler.setInputAction(function(movement) {
        if (!isDragging || !draggedEntity) return;
        
        // 获取新位置
        const ray = viewer.camera.getPickRay(movement.endPosition);
        const newPosition = viewer.scene.globe.pick(ray, viewer.scene);
        
        if (Cesium.defined(newPosition)) {
            // 更新实体位置
            draggedEntity.position = newPosition;
            
            // 检测是否悬停在目标区域上
            checkDropTargets(newPosition);
        }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    
    // 检测目标区域
    function checkDropTargets(position) {
        // 清除之前的悬停目标
        if (hoveredDropTarget) {
            // 恢复原始外观
            hoveredDropTarget.ellipse.material = Cesium.Color.fromAlpha(
                hoveredDropTarget.ellipse.outlineColor.getValue(),
                0.3
            );
            hoveredDropTarget = undefined;
        }
        
        // 获取所有目标区域
        const entities = viewer.entities.values;
        let closestTarget = null;
        let minDistance = Number.MAX_VALUE;
        
        for (let i = 0; i < entities.length; i++) {
            const entity = entities[i];
            
            // 检查是否为目标区域
            if (entity.properties && entity.properties.isDropTarget && 
                entity.properties.isDropTarget.getValue()) {
                
                // 计算拖拽实体与目标区域的距离
                const targetPosition = entity.position.getValue(viewer.clock.currentTime);
                const distance = Cesium.Cartesian3.distance(position, targetPosition);
                
                // 检查是否在目标区域内(半径范围内)
                const radius = entity.properties.radius.getValue();
                
                if (distance < radius && distance < minDistance) {
                    // 检查类型兼容性
                    const acceptTypes = entity.properties.acceptTypes.getValue();
                    const entityType = draggedEntity.properties.type.getValue();
                    
                    if (acceptTypes.includes(entityType) || acceptTypes.includes('*')) {
                        closestTarget = entity;
                        minDistance = distance;
                    }
                }
            }
        }
        
        // 如果找到了有效目标区域
        if (closestTarget) {
            hoveredDropTarget = closestTarget;
            
            // 修改外观以显示可放置
            hoveredDropTarget.ellipse.material = Cesium.Color.fromAlpha(
                Cesium.Color.GREEN,
                0.5
            );
        }
    }
    
    // 鼠标释放处理放置
    handler.setInputAction(function() {
        if (!isDragging || !draggedEntity) return;
        
        // 如果拖拽到了目标区域
        if (hoveredDropTarget) {
            // 执行放置回调
            const onDrop = hoveredDropTarget.properties.onDrop;
            if (onDrop) {
                const callback = onDrop.getValue();
                if (typeof callback === 'function') {
                    callback(draggedEntity, hoveredDropTarget);
                }
            }
            
            // 重置目标区域外观
            hoveredDropTarget.ellipse.material = Cesium.Color.fromAlpha(
                hoveredDropTarget.ellipse.outlineColor.getValue(),
                0.3
            );
        } else {
            // 没有放置到有效区域,恢复原位
            draggedEntity.position = originalPosition;
        }
        
        // 恢复拖拽实体外观
        if (draggedEntity.billboard && draggedEntity._originalScale) {
            draggedEntity.billboard.scale = draggedEntity._originalScale;
            draggedEntity._originalScale = undefined;
        }
        
        // 重置状态
        isDragging = false;
        draggedEntity = undefined;
        originalPosition = undefined;
        hoveredDropTarget = undefined;
        
        // 恢复光标样式
        viewer.canvas.style.cursor = 'default';
        
        // 重新启用相机旋转
        viewer.scene.screenSpaceCameraController.enableRotate = true;
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
    
    // 创建一些示例实体
    createDraggableEntity(
        Cesium.Cartesian3.fromDegrees(116.35, 39.85),
        { text: '拖我-A类', type: 'typeA', value: 100 }
    );
    
    createDraggableEntity(
        Cesium.Cartesian3.fromDegrees(116.40, 39.85),
        { text: '拖我-B类', type: 'typeB', value: 200 }
    );
    
    // 创建目标区域
    createDropTarget(
        Cesium.Cartesian3.fromDegrees(116.35, 39.95),
        {
            text: 'A类目标',
            color: Cesium.Color.BLUE,
            acceptTypes: ['typeA'],
            radius: 300,
            onDrop: function(draggedEntity, target) {
                console.log('A类实体被放置:', draggedEntity.properties.value.getValue());
                // 处理放置逻辑
                target.label.text = target.label.text.getValue() + ' +' + 
                                   draggedEntity.properties.value.getValue();
            }
        }
    );
    
    createDropTarget(
        Cesium.Cartesian3.fromDegrees(116.45, 39.95),
        {
            text: 'B类目标',
            color: Cesium.Color.ORANGE,
            acceptTypes: ['typeB'],
            radius: 300,
            onDrop: function(draggedEntity, target) {
                console.log('B类实体被放置:', draggedEntity.properties.value.getValue());
                // 处理放置逻辑
                target.label.text = target.label.text.getValue() + ' +' + 
                                   draggedEntity.properties.value.getValue();
            }
        }
    );
    
    createDropTarget(
        Cesium.Cartesian3.fromDegrees(116.40, 40.0),
        {
            text: '通用目标',
            color: Cesium.Color.PURPLE,
            acceptTypes: ['*'], // 接受所有类型
            radius: 300,
            onDrop: function(draggedEntity, target) {
                console.log('通用实体被放置:', 
                          draggedEntity.properties.type.getValue(),
                          draggedEntity.properties.value.getValue());
                // 处理放置逻辑
                draggedEntity.show = false; // 隐藏放置的实体
                target.label.text = target.label.text.getValue() + ' +' + 
                                   draggedEntity.properties.value.getValue();
            }
        }
    );
}

// 初始化拖放交互
setupDragAndDropInteraction();

📝 Entity交互最佳实践总结

  1. 创建响应式交互:

    • 为不同的交互设计适当的视觉反馈
    • 使用光标样式变化指示可交互状态
    • 对触摸设备提供适当的交互体验
  2. 性能优化:

    • 减少实体数量,考虑使用Entity clustering
    • 对动态更新的实体使用CallbackProperty缓存值
    • 使用事件节流和防抖技术处理频繁事件
  3. 用户体验提升:

    • 提供清晰的交互反馈(高亮、动画)
    • 设计直观的拖拽和选择行为
    • 提供撤销/恢复操作的能力
  4. 高级功能实现:

    • 将相关实体联系起来实现联动效果
    • 使用事件委托处理大量实体的交互
    • 结合HTML UI提供更丰富的交互体验
  5. 维护和调试:

    • 为交互状态添加适当的日志记录
    • 实现交互状态的可视化调试
    • 采用模块化设计使交互系统易于维护

🧩 记忆助手

实体交互五步骤: 拾取(Pick) → 识别(Identify) → 反馈(Feedback) → 操作(Operate) → 更新(Update)
拖拽实现三要素: 按下(开始) → 移动(更新) → 释放(完成)
事件处理核心类: ScreenSpaceEventHandler处理所有交互


网站公告

今日签到

点亮在社区的每一天
去签到