上一篇我們成功讓3D地球在Vue裏“站穩了腳跟”,但光禿禿的地球顯然滿足不了開發需求——實際項目中,我們常需要在地球表面標記位置、繪製運動軌跡,甚至加載建築物、車輛等3D模型。這一篇,咱們就給地球“加點料”,讓它從“靜態背景”變成“交互載體”,所有代碼都能直接套用上一篇的項目框架,新手也能輕鬆跟進!
一、核心邏輯:Cesium的“實體Entity”是什麼?
在動手前先搞懂一個核心概念——Entity(實體)。Cesium裏的標記點、航線、模型等,本質都是“Entity”的不同表現形式。它就像一個“容器”,可以綁定位置、樣式、屬性等信息,Cesium會自動幫我們渲染到地球對應的經緯度上。
優勢:不用手動操作複雜的底層圖形API,只需通過簡單的配置就能實現複雜效果,這也是Cesium適合新手的原因之一。
小總結:我們接下來的所有操作,核心都是“創建Entity實例並添加到Viewer中”,區別只在Entity的“外觀配置”(點、線、模型)。
二、實戰1:給指定位置加個“標記點”
最常用的功能就是“地理標記”——比如標記公司位置、設備部署點等。我們不僅要畫個點,還要讓它帶標籤、能點擊彈窗,這樣才符合實際開發需求。
2.1 基礎標記點:帶標籤的定位點
打開上一篇創建的src/components/CesiumEarth.vue,在onMounted鈎子的“初始化Viewer”代碼後,直接添加以下代碼(記得先配置好你的AccessToken):
// 1. 基礎標記點(以“北京天安門”為例)
const addMarker = () => {
viewer.entities.add({
name: "北京天安門", // 實體名稱(鼠標懸浮時顯示)
// 核心:定位(經緯度+高度,高度1000米避免被地形遮擋)
position: Cesium.Cartesian3.fromDegrees(116.403874, 39.915119, 1000),
// 點的樣式配置
point: {
color: Cesium.Color.RED, // 顏色:紅色
pixelSize: 12, // 像素大小:12px
outlineColor: Cesium.Color.WHITE, // 邊框顏色:白色
outlineWidth: 2, // 邊框寬度:2px
disableDepthTestDistance: Number.POSITIVE_INFINITY // 始終顯示在最上層
},
// 文字標籤(默認在點的下方)
label: {
text: "天安門", // 標籤文字
font: "16px 微軟雅黑", // 字體樣式
fillColor: Cesium.Color.BLACK, // 文字顏色
pixelOffset: new Cesium.Cartesian2(0, 20), // 相對於點的偏移(x右,y下)
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
1000, // 距離相機1000米以上顯示
500000 // 距離相機500000米以下顯示(避免過遠還能看到小標籤)
)
}
});
};
// 調用函數添加標記點
addMarker();
2.2 進階:點擊標記點彈出詳情彈窗
基礎標記點不夠交互?我們給它加個點擊事件——點擊標記點彈出彈窗,顯示自定義信息(比如地址、設備狀態等)。在上面addMarker函數後,添加彈窗相關代碼:
// 2. 點擊標記點彈出彈窗
const initPopup = () => {
// 監聽“實體點擊”事件
viewer.screenSpaceEventHandler.setInputAction((event) => {
// 檢測點擊位置是否有實體
const pick = viewer.scene.pick(event.position);
if (Cesium.defined(pick) && Cesium.defined(pick.id)) {
const entity = pick.id;
// 只給“名稱包含天安門”的實體觸發彈窗(避免點擊其他元素誤觸發)
if (entity.name.includes("天安門")) {
// 彈窗內容(可自定義HTML,支持圖片、表格等)
const popupContent = `
${entity.name}經緯度:116.403874°E,39.915119°N類型:地標建築
`;
// 關閉已有彈窗(避免多個彈窗疊加)
const oldPopup = document.getElementById("cesiumPopup");
if (oldPopup) oldPopup.remove();
// 創建新彈窗
const popup = document.createElement("div");
popup.id = "cesiumPopup";
popup.innerHTML = popupContent;
popup.style.cssText = `
position: absolute;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
pointer-events: auto; // 允許點擊彈窗內部
z-index: 9999;
`;
// 將彈窗添加到Cesium容器中
const container = document.getElementById("cesiumContainer");
container.appendChild(popup);
// 讓彈窗跟隨鼠標點擊位置
const windowPosition = viewer.scene.cartesianToCanvasCoordinates(entity.position);
popup.style.left = `${windowPosition.x + 10}px`;
popup.style.top = `${windowPosition.y + 10}px`;
}
} else {
// 點擊空白處關閉彈窗
const oldPopup = document.getElementById("cesiumPopup");
if (oldPopup) oldPopup.remove();
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); // 綁定左鍵點擊事件
};
// 調用函數初始化彈窗
initPopup();
效果與説明:
- 啓動項目後,地球會在天安門位置顯示一個紅色標記點,下方有“天安門”文字標籤;
- 點擊標記點會彈出詳情彈窗,點擊地球空白處彈窗自動關閉;
- 彈窗內容支持HTML,你可以改成自己的業務數據(比如接口返回的設備信息)。
三、實戰2:繪製“航線”與“區域”
除了單個點,我們還經常需要繪製“航線”(比如物流路線、航班軌跡)和“區域”(比如管制區域、景區範圍),這兩個功能都基於Entity的“線”和“面”配置。
3.1 繪製發光航線(北京→上海)
在onMounted中添加以下函數,實現一條連接北京和上海的藍色發光航線:
// 3. 繪製航線(北京→上海)
const addRoute = () => {
viewer.entities.add({
name: "北京-上海航線",
// 航線座標數組:[經度1, 緯度1, 高度1, 經度2, 緯度2, 高度2, ...]
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights([
116.403874, 39.915119, 10000, // 起點:北京(高度10公里)
117.200983, 39.084158, 10000, // 途經點:天津
121.473701, 31.230416, 10000 // 終點:上海
]),
width: 5, // 航線寬度
// 發光材質(比普通顏色更醒目)
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.3, // 發光強度
color: Cesium.Color.BLUE // 航線顏色
}),
clampToGround: false // 不貼地(保持固定高度)
}
});
};
// 調用函數添加航線
addRoute();
3.2 繪製區域(以上海迪士尼為例)
用“面”實體繪製一個區域,設置半透明效果,避免遮擋下方地圖:
// 4. 繪製區域(上海迪士尼範圍)
const addArea = () => {
viewer.entities.add({
name: "上海迪士尼",
// 區域頂點座標(閉合多邊形,最後一個點和第一個點重合)
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
121.658344, 31.140015,
121.665813, 31.140208,
121.666006, 31.134364,
121.658537, 31.134171,
121.658344, 31.140015
]),
// 面的樣式(半透明綠色)
material: Cesium.Color.GREEN.withAlpha(0.3), // alpha控制透明度
outline: true, // 顯示區域邊框
outlineColor: Cesium.Color.GREEN, // 邊框顏色
outlineWidth: 2 // 邊框寬度
}
});
};
// 調用函數添加區域
addArea();
注意:繪製區域時,座標數組必須是“閉合的”——最後一個點的經緯度要和第一個點完全一致,否則會出現“缺口”。
四、實戰3:加載3D模型(GLB/GLTF格式)
3D模型是讓地球“立體化”的關鍵,Cesium支持主流的GLB(推薦,單文件包含紋理)和GLTF格式。我們以加載一個飛機模型為例,演示完整流程。
4.1 模型準備與路徑配置
- 獲取測試模型:可以用Cesium官方測試模型(無需下載,直接用鏈接),或自己準備GLB模型;
- 本地模型路徑:如果用自己的模型,將GLB文件放到項目的
public目錄下(比如public/models/plane.glb),引用路徑用/models/plane.glb。
4.2 加載模型到地球
在onMounted中添加以下代碼,將飛機模型加載到“北京”上空:
// 5. 加載3D模型(飛機模型)
const addModel = () => {
viewer.entities.add({
name: "飛機模型",
// 模型位置(北京上空2公里)
position: Cesium.Cartesian3.fromDegrees(116.403874, 39.915119, 2000),
// 模型配置
model: {
uri: "https://sandcastle.cesium.com/SampleData/models/CesiumAir/Cesium_Air.glb", // 官方測試模型鏈接
// 本地模型路徑示例:uri: "/models/plane.glb",
scale: 500, // 縮放比例(根據模型大小調整,避免過大或過小)
minimumPixelSize: 128, // 最小像素大小(避免拉遠視角後模型消失)
maximumScale: 2000, // 最大縮放比例(避免拉近視角後模型過大)
// 讓模型始終朝向相機(可選)
billboard: {
image: "https://sandcastle.cesium.com/SampleData/models/CesiumAir/cesium_air.png",
scale: 0.5,
show: false // 模型可見時隱藏圖片
}
},
// 模型旋轉(讓飛機機頭朝向上海方向)
orientation: new Cesium.VelocityOrientationProperty(
new Cesium.SampledPositionProperty(Cesium.Cartesian3)
)
});
// 視角定位到模型(方便啓動後直接看到模型)
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.403874, 39.915119, 5000), // 距離模型5公里
duration: 3 // 飛行時間3秒(平滑過渡)
});
};
// 調用函數加載模型
addModel();
模型加載避坑要點:
- 路徑錯誤:本地模型必須放
public目錄,路徑以“/”開頭(比如/models/plane.glb),不能用@/models; - 模型過小/過大:通過
scale調整比例,配合minimumPixelSize保證模型不會“消失”; - 紋理丟失:優先用GLB格式(紋理嵌入在文件內),GLTF格式需要單獨放紋理圖片,且路徑要正確。
五、功能整合與效果預覽
將上面所有函數整合到CesiumEarth.vue的onMounted中,完整代碼結構如下(核心部分):
onMounted(() => {
// 1. 配置AccessToken(上一篇的代碼)
Cesium.Ion.defaultAccessToken = '你的令牌';
// 2. 初始化Viewer(上一篇的代碼)
viewer = new Cesium.Viewer('cesiumContainer', { ... });
// 3. 優化場景配置(上一篇的代碼)
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true;
// 4. 新增功能:依次調用各函數
addMarker(); // 標記點
initPopup(); // 彈窗交互
addRoute(); // 航線
addArea(); // 區域
addModel(); // 3D模型
});
啓動項目後,你會看到:
✅ 天安門位置有紅色標記點,點擊彈出詳情;
✅ 北京到上海有一條藍色發光航線,途經天津;
✅ 上海迪士尼區域有半透明綠色區塊;
✅ 北京上空有一個飛機3D模型,視角會自動飛行到模型位置。
六、新手必踩坑與解決方案
1. 標記點/模型“鑽到地下”看不見?
問題:位置的“高度”設置過低,被地形遮擋。
解決:將position的高度參數調大(比如從100改為1000),或開啓地形檢測(上一篇的depthTestAgainstTerrain: true)。
2. 航線“貼地”彎曲,不是直線?
問題:clampToGround屬性設為了true,航線會貼合地形起伏。
解決:將polyline中的clampToGround改為false,並設置固定高度。
3. 模型加載後“一片黑”?
問題:模型缺少光照,或紋理路徑錯誤。
解決:
- 添加光照:
scene.globe.enableLighting = true;; - 換用官方測試模型(排除本地模型問題)。
4. 彈窗位置“跑偏”?
問題:地球旋轉後,彈窗沒有跟隨標記點位置更新。
解決:監聽scene.preRender事件,實時更新彈窗位置(進階功能,代碼如下):
// 在initPopup函數末尾添加
viewer.scene.preRender.addEventListener(() => {
const popup = document.getElementById("cesiumPopup");
const entity = viewer.selectedEntity;
if (popup && entity && entity.name.includes("天安門")) {
const windowPosition = viewer.scene.cartesianToCanvasCoordinates(entity.position);
popup.style.left = `${windowPosition.x + 10}px`;
popup.style.top = `${windowPosition.y + 10}px`;
}
});