vue+cesium示例:地形开挖(附源码下载) - 详解

基于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

技术栈

Vue3.5.13

Vite 6.2.0

Cesium 1.128.0

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

<template>
  <div id="cesiumContainer" class=
  "cesium-container">
  <
  !-- 模型调整控制面板 -->
  <div class=
  "control-panel">
  <div class=
  "panel-header">
  <h3>地形开挖<
    /h3>
    <
    /div>
    <div class=
    "panel-body">
    <div class=
    "control-group">
    <button @click="startDrawPolygon">绘制多边形<
      /button>
      <
      /div>
      <div class=
      "control-group">
      <button @click="clearDrawing">清除绘制<
        /button>
        <
        /div>
        <div class=
        "control-group" v-if="drawingInstructions">
        <span>
          {
          { drawingInstructions
          }
          }<
          /span>
          <
          /div>
          <
          /div>
          <
          /div>
          <
          /div>
          <
          /template>
          <script setup>
            import { onMounted, onUnmounted, ref
            } from 'vue';
            import * as Cesium from 'cesium';
            Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxZjhjYjhkYS1jMzA1LTQ1MTEtYWE1Mi0zODc5NDljOGVkMDYiLCJpZCI6MTAzNjEsInNjb3BlcyI6WyJhc2wiLCJhc3IiLCJhc3ciLCJnYyJdLCJpYXQiOjE1NzA2MDY5ODV9.X7tj92tunUvx6PkDpj3LFsMVBs_SBYyKbIL_G9xKESA';
            // 声明Cesium Viewer实例
            let viewer = null;
            // 声明变量用于存储事件处理器和绘制状态
            let handler = null;
            let activeShapePoints = [];
            let activeShape = null;
            let floatingPoint = null;
            let excavateInstance = null;
            // 绘制状态提示
            const drawingInstructions = ref('');
            // 组件挂载后初始化Cesium
            onMounted(async () =>
            {
            const files = [
            "./excavateTerrain/excavateTerrain.js"
            ];
            loadScripts(files, function () {
            console.log("All scripts loaded");
            initMap();
            });
            });
            const loadScripts = (files, callback) =>
            {
            // Make Cesium available globally for the scripts
            window.Cesium = Cesium;
            if (files.length === 0) {
            callback();
            return;
            }
            const file = files.shift();
            const script = document.createElement("script");
            script.onload = function () {
            loadScripts(files, callback);
            };
            script.src = file;
            document.head.appendChild(script);
            };
            const initMap = 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场景
            terrain: Cesium.Terrain.fromWorldTerrain(), // 使用世界地形
            });
            // 隐藏logo
            viewer.cesiumWidget.creditContainer.style.display = "none";
            viewer.scene.globe.enableLighting = true;
            // 禁用大气层和太阳
            viewer.scene.skyAtmosphere.show = false;
            //前提先把场景上的图层全部移除或者隐藏 
            // viewer.scene.globe.baseColor = Cesium.Color.BLACK; //修改地图蓝色背景
            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, 10000000.0), // 中国中部上空
            orientation: {
            heading: 0.0,
            pitch: -Cesium.Math.PI_OVER_TWO,
            roll: 0.0
            }
            });
            // 启用地形深度测试,确保地形正确渲染
            viewer.scene.globe.depthTestAgainstTerrain = true;
            // 清除默认地形
            // viewer.scene.terrainProvider = new Cesium.EllipsoidTerrainProvider({});
            // 开启帧率
            viewer.scene.debugShowFramesPerSecond = true;
            }
            // 开始绘制多边形
            const startDrawPolygon = () =>
            {
            // 清除之前的绘制
            clearDrawing();
            // 设置绘制提示
            drawingInstructions.value = '左键点击添加顶点,右键完成绘制';
            // 创建新的事件处理器
            handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
            // 监听左键点击事件 - 添加顶点
            handler.setInputAction((click) =>
            {
            // 获取点击位置的笛卡尔坐标
            const cartesian = viewer.scene.pickPosition(click.position);
            if (!Cesium.defined(cartesian)) {
            return;
            }
            // 将点添加到活动形状点数组
            activeShapePoints.push(cartesian);
            // 如果是第一个点,创建浮动点
            if (activeShapePoints.length === 1) {
            floatingPoint = createPoint(cartesian);
            // 创建动态多边形
            activeShape = createPolygon(activeShapePoints);
            }
            }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
            // 监听鼠标移动事件 - 更新动态多边形
            handler.setInputAction((movement) =>
            {
            if (activeShapePoints.length >= 1) {
            const cartesian = viewer.scene.pickPosition(movement.endPosition);
            if (!Cesium.defined(cartesian)) {
            return;
            }
            // 更新浮动点位置
            if (floatingPoint) {
            floatingPoint.position.setValue(cartesian);
            }
            // 更新动态多边形
            if (activeShape) {
            const positions = activeShapePoints.concat([cartesian]);
            activeShape.polygon.hierarchy = new Cesium.PolygonHierarchy(positions);
            }
            }
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
            // 监听右键点击事件 - 完成绘制
            handler.setInputAction(() =>
            {
            if (activeShapePoints.length <
            3) {
            // 至少需要3个点才能形成多边形
            drawingInstructions.value = '至少需要3个点才能形成多边形,请继续绘制';
            return;
            }
            // 完成绘制
            terminateShape();
            // 执行地形开挖
            performExcavation(activeShapePoints);
            // 清除绘制状态
            drawingInstructions.value = '绘制完成,地形已开挖';
            // 清除事件处理器
            handler.destroy();
            handler = null;
            }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
            };
            // 创建点实体
            const createPoint = (position) =>
            {
            return viewer.entities.add({
            position: position,
            point: {
            color: Cesium.Color.WHITE,
            pixelSize: 10,
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
            }
            });
            };
            // 创建多边形实体
            const createPolygon = (positions) =>
            {
            return viewer.entities.add({
            polygon: {
            hierarchy: new Cesium.PolygonHierarchy(positions),
            material: new Cesium.ColorMaterialProperty(Cesium.Color.WHITE.withAlpha(0.3)),
            outline: true,
            outlineColor: Cesium.Color.WHITE,
            // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
            // 关键属性:贴地模式
            clampToGround: true,
            // 禁用挤压高度
            // height: 0,
            // height: 0,
            // perPositionHeight: false,
            // classificationType: Cesium.ClassificationType.BOTH,
            // zIndex: 100
            }
            });
            };
            // 完成绘制形状
            const terminateShape = () =>
            {
            // 移除动态实体
            if (floatingPoint) {
            viewer.entities.remove(floatingPoint);
            floatingPoint = null;
            }
            if (activeShape) {
            viewer.entities.remove(activeShape);
            activeShape = null;
            }
            // 创建最终的多边形
            // if (activeShapePoints.length >= 3) {
            // const finalPolygon = viewer.entities.add({
            // polygon: {
            // hierarchy: new Cesium.PolygonHierarchy(activeShapePoints),
            // material: new Cesium.ColorMaterialProperty(Cesium.Color.GREEN.withAlpha(0.3)),
            // outline: true,
            // outlineColor: Cesium.Color.GREEN,
            // height: 0,
            // perPositionHeight: false,
            // classificationType: Cesium.ClassificationType.BOTH,
            // zIndex: 100
            // }
            // });
            // }
            };
            // 执行地形开挖
            const performExcavation = (positions) =>
            {
            // 如果已有开挖实例,先尝试清除
            if (excavateInstance) {
            try {
            // 重置地形开挖
            viewer.scene.globe.clippingPlanes = undefined;
            viewer.entities.removeById("entityDM");
            viewer.entities.removeById("entityDMBJ");
            } catch (error) {
            console.error("清除之前的开挖失败", error);
            }
            }
            // 创建新的地形开挖实例
            excavateInstance = new excavateTerrain(viewer, {
            positions: positions,
            height: 50,
            bottom: "./excavateTerrain/excavationregion_side.jpg",
            side: "./excavateTerrain/excavationregion_top.jpg",
            });
            };
            // 清除绘制
            const clearDrawing = () =>
            {
            // 清除事件处理器
            if (handler) {
            handler.destroy();
            handler = null;
            }
            // 清除动态实体
            if (floatingPoint) {
            viewer.entities.remove(floatingPoint);
            floatingPoint = null;
            }
            if (activeShape) {
            viewer.entities.remove(activeShape);
            activeShape = null;
            }
            // 清空点数组
            activeShapePoints = [];
            viewer.entities.removeAll();
            // 清除绘制提示
            drawingInstructions.value = '';
            // 重置地形开挖
            if (excavateInstance) {
            try {
            viewer.scene.globe.clippingPlanes = undefined;
            viewer.entities.removeById("entityDM");
            viewer.entities.removeById("entityDMBJ");
            excavateInstance = null;
            } catch (error) {
            console.error("清除地形开挖失败", error);
            }
            }
            };
            // 组件卸载前清理资源
            onUnmounted(() =>
            {
            clearDrawing();
            if (viewer) {
            viewer.destroy();
            viewer = null;
            }
            });
            <
            /script>
            <style scoped>
              .cesium-container {
              width: 100%;
              height: 100vh;
              margin: 0;
              padding: 0;
              overflow: hidden;
              position: relative;
              }
              .control-panel {
              position: absolute;
              top: 20px;
              left: 20px;
              width: 300px;
              background-color: rgba(38, 38, 38, 0.85);
              border-radius: 8px;
              box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
              color: white;
              z-index: 1000;
              overflow: hidden;
              transition: all 0.3s ease;
              }
              .panel-header {
              background-color: rgba(0, 0, 0, 0.5);
              padding: 10px 15px;
              border-bottom: 1px solid rgba(255, 255, 255, 0.1);
              }
              .panel-header h3 {
              margin: 0;
              font-size: 16px;
              font-weight: 500;
              }
              .panel-body {
              padding: 15px;
              }
              .control-group {
              margin-bottom: 15px;
              }
              .control-group label {
              display: block;
              margin-bottom: 5px;
              font-size: 14px;
              }
              .control-group input[type="range"] {
              width: 100%;
              margin-bottom: 5px;
              background-color: rgba(255, 255, 255, 0.2);
              border-radius: 4px;
              }
              .control-group span {
              font-size: 12px;
              color: rgba(255, 255, 255, 0.7);
              display: block;
              margin-top: 5px;
              line-height: 1.4;
              }
              .control-group span {
              font-size: 12px;
              color: rgba(255, 255, 255, 0.7);
              }
              .control-group button {
              background-color: #4285f4;
              color: white;
              border: none;
              padding: 8px 15px;
              border-radius: 4px;
              cursor: pointer;
              font-size: 14px;
              width: 100%;
              transition: background-color 0.3s ease;
              }
              .control-group button:hover {
              background-color: #3367d6;
              }
              <
              /style>
posted on 2025-07-17 10:30  ljbguanli  阅读(7)  评论(0)    收藏  举报