返回 导航

Vue.js

hangge.com

Vue.js - 集成Cesium构建三维可视化场景教程5(添加图片图标实体)

作者:hangge | 2026-07-07 10:03
    前文我介绍了如何创建常见实体(标签/点/面/线/体)并添加到场景中。本文我将接着演示如何在 Vue.js 中为 Cesium 场景添加图片图标(Billboard)。

五、添加图片图标实体

1,样例代码

下面代码实现在指定地理位置动态添加图片标注(Billboard)及带文字说明的图片标注。
<template>
    <div class="layer-container">
        <div ref="cesiumContainer" class="cesium-container"></div>

        <!-- 加载状态 -->
        <div v-if="loading" class="loading-overlay">
            <div class="loading-spinner"></div>
            <p>加载三维场景中...</p>
        </div>

        <!-- 工具栏 -->
        <div class="toolbar" v-if="!loading">
            <button @click="addBillboardEntity1">添加图片</button>
            <button @click="addBillboardEntity2">添加带标签图片</button>
            <button @click="removeAllEntities">清除所有实体</button>
        </div>
    </div>
</template>

<script>
// 引入 Cesium 核心类
import * as Cesium from "cesium";

export default {
    name: 'CesiumViewer',

    data() {
        return {
            viewer: null,
            loading: true,
            terrainEnabled: true,
            sampleEntities: [],
        }
    },

    mounted() {
        // 确保 Cesium 已加载
        if (typeof Cesium === 'undefined') {
            console.error('Cesium 未加载,请检查配置')
            this.loading = false
            return
        }

        // 初始化 Cesium 场景
        this.initCesium()
    },

    beforeDestroy() {
        // 组件销毁时清理资源
        this.destroyCesium()
    },

    methods: {
        // 初始化 Cesium
        async initCesium() {
            try {
                // 配置 Cesium 访问令牌(如果需要,从 Cesium Ion 获取)
                //Cesium.Ion.defaultAccessToken = '你的Cesium Ion访问令牌'

                // 创建 Viewer 实例
                this.viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
                    animation: false, // 是否显示动画控件
                    baseLayerPicker: false, // 是否显示图层选择控件
                    fullscreenButton: false, // 是否显示全屏按钮
                    geocoder: false, // 是否显示地理编码搜索控件
                    homeButton: false, // 是否显示Home按钮
                    infoBox: false, // 是否显示信息框
                    sceneModePicker: false, // 是否显示3D/2D选择器
                    selectionIndicator: false, // 是否显示选取指示器组件
                    timeline: false, // 是否显示时间轴
                    navigationHelpButton: false, // 是否显示帮助按钮
                    scene3DOnly: true, // 每个几何实例仅以3D渲染以节省GPU内存
                    shouldAnimate: true, // 自动播放动画
                    // 地形配置
                    terrainProvider: await Cesium.createWorldTerrainAsync()
                })

                // 强制重新计算画布尺寸
                this.viewer.canvas.width = this.viewer.container.clientWidth;
                this.viewer.canvas.height = this.viewer.container.clientHeight;
                this.viewer.resize(); // 核心方法:告知 Cesium 容器大小已变

                // 设置初始视图
                this.setInitialView()

                // 加载完成
                this.loading = false

                console.log('Cesium 场景初始化完成')
            } catch (error) {
                console.error('初始化 Cesium 失败:', error)
                this.loading = false
            }
        },

        // 设置初始视图
        setInitialView() {
            // 设置初始位置(例如:北京天安门)
            const initialPosition = Cesium.Cartesian3.fromDegrees(116.3914, 39.9074, 1100)
            this.viewer.camera.setView({
                destination: initialPosition,
                orientation: {
                    heading: Cesium.Math.toRadians(0),
                    pitch: Cesium.Math.toRadians(-90),
                    roll: 0
                }
            })
        },

        // 添加图片
        addBillboardEntity1() {
            const billboard = this.viewer.entities.add({
                name: '图片',
                position: Cesium.Cartesian3.fromDegrees(116.3914, 39.9062),
                billboard: {
                    image: '/images/construction.png',
                    scale: 0.2,
                    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY // 强制始终可见
                }
            })
            this.sampleEntities.push(billboard)
        },
        
        // 添加带标签图片
        addBillboardEntity2() {
            const billboard = this.viewer.entities.add({
                name: '图片',
                position: Cesium.Cartesian3.fromDegrees(116.3932, 39.9062),
                billboard: {
                    image: '/images/construction.png',
                    scale: 0.2,
                    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY // 强制始终可见
                },
                label: {
                    text: '正在施工',
                    font: '14px sans-serif',
                    verticalOrigin: Cesium.VerticalOrigin.TOP,
                    pixelOffset: new Cesium.Cartesian2(0, 10),
                    fillColor: Cesium.Color.WHITE,
                    outlineColor: Cesium.Color.BLACK,
                    outlineWidth: 2,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
                }
            })
            this.sampleEntities.push(billboard)
        },

        // 清空所有实体
        removeAllEntities() {
            this.viewer.entities.removeAll();
            this.sampleEntities = [];
        },

        // 销毁 Cesium 资源
        destroyCesium() {
            if (this.viewer) {
                this.viewer.destroy()
                this.viewer = null
            }
        }
    }
}
</script>

<style scoped>
.layer-container {
    position: relative;
    width: 100%;
    height: 100%;
}


.cesium-container {
    width: 100%;
    height: 100%;
}

.loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    z-index: 1000;
    color: white;
}

.loading-spinner {
    border: 4px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    border-top: 4px solid #42b983;
    width: 50px;
    height: 50px;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

.toolbar {
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 999;
    display: flex;
    gap: 5px;
}

.toolbar button {
    padding: 8px 15px;
    background: rgba(40, 44, 52, 0.8);
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    transition: background 0.3s;
}

.toolbar button:hover {
    background: rgba(66, 185, 131, 0.8);
}
</style>

2,运行效果

(1)点击工具栏“添加图片”按钮会在指定地理位置处生成一个图片标注(Billboard),显示为一张施工图标,图标底部锚定在地面位置并始终面向屏幕,通过关闭深度测试确保在地形或其他对象遮挡时依然清晰可见。

(2)点击工具栏“添加带标签图片”按钮除了会在另一处指定位置生成一个图片标注,还会在图片上方同步显示文字标签“正在施工”,图片与文字保持空间关联,标签具有描边效果,始终朝向视角且不被遮挡。

(3)点击工具栏“清除所有实体”按钮会移除当前场景中已添加的所有图片及带标签的图片实体,三维场景恢复为初始状态。
评论

全部评论(0)

回到顶部