vue+cesium示例:实现动态绘制几何体设置颜色拖动效果(附源码下载)

接到一位知识星友的邀请,他那边的需求是需要动态绘制几何体,并且可以设置颜色以及拖动。所以,本篇的demo是基于cesium和vue实现动态绘制几何体设置颜色拖动效果,适合学习Cesium与前端框架结合开发3D可视化项目。

demo源码运行环境以及配置

运行环境:依赖Node安装环境,demo本地Node版本:推荐v18+。

运行工具:vscode或者其他工具。

配置方式:下载demo源码,vscode打开,然后顺序执行以下命令: (1)下载demo环境依赖包命令:npm install
(2)启动demo命令:npm run dev (3)打包demo命令: npm run build

技术栈

Vue 3.5.13

Vite 6.2.0

Cesium 1.128.0

示例效果
在这里插入图片描述
核心源码

<template>
    <div id="cesiumContainer" class="cesium-container"></div>
    <div class="toolsDiv">
        <el-button type="primary" @click="drawCube">
            绘制立方体
        </el-button>
        <el-button type="primary" @click="drawSphere">
            绘制球体
        </el-button>
        <el-button type="primary" @click="drawEllipsoid">
            绘制椭圆体
        </el-button>
        <el-button type="primary" @click="drawCylinder">
            绘制圆柱体
        </el-button>
        <el-button type="primary" @click="drawCone">
            绘制圆锥体
        </el-button>
        <el-button type="danger" @click="clearGeometry">
            清除几何体
        </el-button>
    </div>
    <div class="color-picker-panel" v-if="selectedEntity">
        <el-color-picker v-model="selectedColor" @change="updateEntityColor" show-alpha></el-color-picker>
    </div>
</template>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import * as Cesium from 'cesium';
// 声明Cesium Viewer实例
let viewer = null;
// 声明变量用于存储事件处理器和实体集合
let handler = null;
let geometryEntities = [];
// 声明当前绘制模式
const drawingMode = ref(null);
// 声明当前选中的实体
const selectedEntity = ref(null);
// 声明选中的颜色
const selectedColor = ref('rgba(255, 0, 0, 0.7)'); // 默认颜色,与立方体一致
// 声明拖动状态
const isDragging = ref(false);
let dragHandler = null;
// 不再需要动画帧相关变量
// 节流函数 - 限制函数执行频率
const throttle = (fn, delay) => {
    let lastCall = 0;
    return function (...args) {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            return fn.apply(this, args);
        }
    };
};
// 组件挂载后初始化Cesium
onMounted(async () => {
    // 初始化Cesium Viewer
    viewer = new Cesium.Viewer('cesiumContainer', {
        // 基础配置
        animation: false, // 动画小部件
        baseLayerPicker: false, // 底图选择器
        fullscreenButton: false, // 全屏按钮
        vrButton: false, // VR按钮
        geocoder: false, // 地理编码搜索框
        homeButton: false, // 主页按钮
        infoBox: false, // 信息框
        sceneModePicker: false, // 场景模式选择器
        selectionIndicator: false, // 选择指示器
        timeline: false, // 时间轴
        navigationHelpButton: false, // 导航帮助按钮
        navigationInstructionsInitiallyVisible: false, // 导航说明初始可见性
        scene3DOnly: false, // 仅3D场景
    });
    // 隐藏logo
    viewer.cesiumWidget.creditContainer.style.display = "none";
    viewer.scene.globe.enableLighting = false;
    //前提先把场景上的图层全部移除或者隐藏 
    viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.1, 0.2, 1.0); //修改地图为暗蓝色背景
    // 设置抗锯齿
    viewer.scene.postProcessStages.fxaa.enabled = true;
    // 清除默认底图
    viewer.imageryLayers.remove(viewer.imageryLayers.get(0));
    // 加载底图 - 使用更暗的地图服务
    const imageryProvider = await Cesium.ArcGisMapServerImageryProvider.fromUrl("https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer");
    viewer.imageryLayers.addImageryProvider(imageryProvider);
    // 设置默认视图位置 - 默认显示全球视图
    viewer.camera.setView({
        destination: Cesium.Cartesian3.fromDegrees(104.0, 30.0, 10000.0), // 中国中部上空
        orientation: {
            heading: 0.0,
            pitch: -Cesium.Math.PI_OVER_TWO,
            roll: 0.0
        }
    });
// 绘制立方体
const drawCube = () => {
    // 清除之前的事件处理器
    if (handler) {
        handler.destroy();
    }
    // 设置当前绘制模式
    drawingMode.value = 'cube';
    // 创建新的事件处理器
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    // 监听左键点击事件
    handler.setInputAction((click) => {
        // 获取点击位置的笛卡尔坐标
        const cartesian = viewer.scene.pickPosition(click.position);
        if (Cesium.defined(cartesian)) {
            // 创建立方体实体
            const entity = viewer.entities.add({
                position: cartesian,
                box: {
                    dimensions: new Cesium.Cartesian3(1000.0, 1000.0, 1000.0), // 尺寸(米)
                    material: Cesium.Color.RED.withAlpha(0.7), // 半透明红色
                    outline: true,
                    outlineColor: Cesium.Color.TRANSPARENT,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 贴地
                }
            });
            // 将实体添加到集合中,以便后续清除
            geometryEntities.push(entity);
            // 清除事件处理器
            handler.destroy();
            handler = null;
            drawingMode.value = null;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 绘制球体
const drawSphere = () => {
    // 清除之前的事件处理器
    if (handler) {
        handler.destroy();
    }
    // 设置当前绘制模式
    drawingMode.value = 'sphere';
    // 创建新的事件处理器
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    // 监听左键点击事件
    handler.setInputAction((click) => {
        // 获取点击位置的笛卡尔坐标
        const cartesian = viewer.scene.pickPosition(click.position);
        if (Cesium.defined(cartesian)) {
            // 创建球体实体
            const entity = viewer.entities.add({
                position: cartesian,
                ellipsoid: {
                    radii: new Cesium.Cartesian3(500.0, 500.0, 500.0), // 半径(米)
                    material: Cesium.Color.BLUE.withAlpha(0.7), // 半透明蓝色
                    outline: true,
                    outlineColor: Cesium.Color.TRANSPARENT,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 贴地
                }
            });
            // 将实体添加到集合中,以便后续清除
            geometryEntities.push(entity);
            // 清除事件处理器
            handler.destroy();
            handler = null;
            drawingMode.value = null;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 绘制椭圆体
const drawEllipsoid = () => {
    // 清除之前的事件处理器
    if (handler) {
        handler.destroy();
    }
    // 设置当前绘制模式
    drawingMode.value = 'ellipsoid';
    // 创建新的事件处理器
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    // 监听左键点击事件
    handler.setInputAction((click) => {
        // 获取点击位置的笛卡尔坐标
        const cartesian = viewer.scene.pickPosition(click.position);
        if (Cesium.defined(cartesian)) {
            // 创建椭圆体实体
            const entity = viewer.entities.add({
                position: cartesian,
                ellipsoid: {
                    radii: new Cesium.Cartesian3(800.0, 500.0, 300.0), // 不同轴的半径(米)
                    material: Cesium.Color.GREEN.withAlpha(0.7), // 半透明绿色
                    outline: true,
                    outlineColor: Cesium.Color.TRANSPARENT,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 贴地
                }
            });
            // 将实体添加到集合中,以便后续清除
            geometryEntities.push(entity);
            // 清除事件处理器
            handler.destroy();
            handler = null;
            drawingMode.value = null;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 绘制圆柱体
const drawCylinder = () => {
    // 清除之前的事件处理器
    if (handler) {
        handler.destroy();
    }
    // 设置当前绘制模式
    drawingMode.value = 'cylinder';
    // 创建新的事件处理器
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    // 监听左键点击事件
    handler.setInputAction((click) => {
        // 获取点击位置的笛卡尔坐标
        const cartesian = viewer.scene.pickPosition(click.position);
        if (Cesium.defined(cartesian)) {
            // 创建圆柱体实体
            const entity = viewer.entities.add({
                position: cartesian,
                cylinder: {
                    length: 1000.0, // 长度(米)
                    topRadius: 300.0, // 顶部半径(米)
                    bottomRadius: 300.0, // 底部半径(米)
                    material: Cesium.Color.ORANGE.withAlpha(0.7), // 半透明橙色
                    outline: true,
                    outlineColor: Cesium.Color.TRANSPARENT,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 贴地
                }
            });
            // 将实体添加到集合中,以便后续清除
            geometryEntities.push(entity);
            // 清除事件处理器
            handler.destroy();
            handler = null;
            drawingMode.value = null;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 绘制圆锥体
const drawCone = () => {
    // 清除之前的事件处理器
    if (handler) {
        handler.destroy();
    }
    // 设置当前绘制模式
    drawingMode.value = 'cone';
    // 创建新的事件处理器
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    // 监听左键点击事件
    handler.setInputAction((click) => {
        // 获取点击位置的笛卡尔坐标
        const cartesian = viewer.scene.pickPosition(click.position);
        if (Cesium.defined(cartesian)) {
            // 创建圆锥体实体(使用圆柱体,顶部半径为0)
            const entity = viewer.entities.add({
                position: cartesian,
                cylinder: {
                    length: 1000.0, // 长度(米)
                    topRadius: 0.0, // 顶部半径为0(米)
                    bottomRadius: 400.0, // 底部半径(米)
                    material: Cesium.Color.PURPLE.withAlpha(0.7), // 半透明紫色
                    outline: true,
                    outlineColor: Cesium.Color.TRANSPARENT,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 贴地
                }
            });
            // 将实体添加到集合中,以便后续清除
            geometryEntities.push(entity);
            // 清除事件处理器
            handler.destroy();
            handler = null;
            drawingMode.value = null;
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 清除所有几何体
const clearGeometry = () => {
    // 清除所有几何体实体
    for (let i = 0; i < geometryEntities.length; i++) {
        viewer.entities.remove(geometryEntities[i]);
    }
    // 清空实体集合
    geometryEntities = [];
    // 清除事件处理器
    if (handler) {
        handler.destroy();
        handler = null;
    }
    // 重置绘制模式
    drawingMode.value = null;
};
 // 添加用于选择和拖动实体的事件处理器
    dragHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    // 左键按下事件:选择实体并准备拖动
    dragHandler.setInputAction((movement) => {
        const pickedObject = viewer.scene.pick(movement.position);
        // if (Cesium.defined(pickedObject) && pickedObject.id instanceof Cesium.Entity && geometryEntities.includes(pickedObject.id)) {
        if (Cesium.defined(pickedObject) && pickedObject.id instanceof Cesium.Entity) {
            selectedEntity.value = pickedObject.id;
            isDragging.value = true;
            viewer.scene.screenSpaceCameraController.enableRotate = false; // 禁用相机旋转
            viewer.scene.canvas.style.cursor = 'move'; // 设置鼠标样式为移动
            // 获取当前实体的颜色并设置给颜色选择器
            let currentColor;
            if (selectedEntity.value.box) {
                currentColor = selectedEntity.value.box.material.color.getValue(viewer.clock.currentTime);
            } else if (selectedEntity.value.ellipsoid) {
                currentColor = selectedEntity.value.ellipsoid.material.color.getValue(viewer.clock.currentTime);
            } else if (selectedEntity.value.cylinder) {
                currentColor = selectedEntity.value.cylinder.material.color.getValue(viewer.clock.currentTime);
            }
            if (currentColor) {
                selectedColor.value = currentColor.toCssColorString();
            }
        } else {
            selectedEntity.value = null; // 点击空白区域取消选择
            isDragging.value = false;
            viewer.scene.canvas.style.cursor = 'default'; // 恢复默认鼠标样式
        }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    // 鼠标移动事件:计算新位置但不直接更新
    dragHandler.setInputAction((movement) => {
        if (isDragging.value && selectedEntity.value) {
            calculatePosition(movement.endPosition);
        }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    // 左键抬起事件:结束拖动
    dragHandler.setInputAction(() => {
        if (isDragging.value) { // 仅在拖动状态下执行
            isDragging.value = false;
            viewer.scene.screenSpaceCameraController.enableRotate = true; // 恢复相机旋转
            viewer.scene.canvas.style.cursor = 'default'; // 恢复默认鼠标样式
        }
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
    // 注意:这里我们不在 onUnmounted 中单独处理 dragHandler,因为它会在 viewer destroy 时自动清理
    // 但为了代码清晰,可以显式管理,或者依赖 Cesium 的清理机制
});
……
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GIS之家

赠人玫瑰,手有余香

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值