Cesium 8 ,在 Cesium 上实现雷达动画和车辆动画效果,并控制显示和隐藏

article/2025/8/25 2:43:58

目录

✨前言
一、功能背景
1.1 核心功能概览
1.2 技术栈与工具
二、车辆动画
2.1 模型坐标
2.2 组合渲染
2.3 显隐状态
2.4 模型文件
三、雷达动画
3.1 创建元素
3.2 动画解析
3.3 坐标联动
3.4 交互事件
四、完整代码
4.1 属性参数
4.2 逻辑代码
加载车辆动画
控制车辆动画
加载雷达动画
控制雷达动画
4.3 模拟展示
五、注意事项
5.1 资源加载与路径问题
5.2 性能与内存管理
5.3 兼容性与调试
六、本文总结
七、更多操作

✨前言

在《Cesium 7,在 Cesium 上实现高速道路渲染》中,我们完成了高速道路的三维建模与场景搭建。本文作为续篇,将聚焦于动态元素与交互逻辑的开发,在已构建的道路场景中叠加雷达动画与车辆模型,实现 “静态场景 + 动态实体” 的完整可视化方案。通过本文分享,你将学会如何在 Cesium 道路场景中集成动态交互元素,并掌握坐标转换、动画控制与状态管理的核心技术。


 

一、功能背景

功能实现效果与技术架构

1.1 核心功能概览

  1. 车辆模型系统:加载 GLB 格式 3D 车辆模型,附带标签文本与动态扩散波纹,模拟救援车辆的实时位置。
  2. 雷达动画系统:基于 HTML+CSS 实现圆形波纹扩散效果,绑定经纬度坐标,支持点击交互触发事件弹窗。
  3. 交互控制:通过统一接口切换元素显隐状态,结合 Cesium 坐标转换实现动画元素与地图视图的动态联动。

1.2 技术栈与工具

  • 框架:CesiumJS(三维渲染引擎)、JavaScript(逻辑控制)。
  • 资源格式:GLB(3D 模型)、CSS3(动画样式)。
  • 关键 API
    1. Cesium.Cartesian3.fromDegrees:经纬度转笛卡尔坐标。
    2. viewer.entities.add:添加模型、标签、波纹等实体。
    3. CallbackProperty:动态更新椭圆波纹的半径与透明度。


二、车辆动画

车辆模型动画开发详解

2.1 模型坐标

模型加载与坐标计算

代码实现

loadVehicleModelAnime() {// 初始经纬度(车辆位置)const initLng = this.RealCarLonLat.lon;const initLat = this.RealCarLonLat.lat;// 移动距离与地球半径(用于坐标偏移计算)const moveDistance = 20; // 20米const earthRadius = 6371000; // 地球半径(米)// 计算偏移量(简化版航向角为0°,仅水平移动)const deltaLat = (moveDistance / earthRadius) * Math.cos(0);const deltaLng = (moveDistance / (earthRadius * Math.cos(Cesium.Math.toRadians(initLat)))) * Math.sin(0);const newLat = initLat + Cesium.Math.toDegrees(deltaLat);const newLng = initLng + Cesium.Math.toDegrees(deltaLng);// 目标位置(笛卡尔坐标)const targetPosition = Cesium.Cartesian3.fromDegrees(newLng, newLat, 0);// ... 后续模型实体创建代码
}

原理说明

  1. 坐标偏移逻辑:通过球面几何公式计算车辆在平面地图上的偏移量,deltaLat 和 deltaLng 分别对应纬度和经度方向的位移弧度,最终转换为度数生成目标坐标。
  2. 航向角扩展:若需车辆朝向动态变化,可将 heading 参数与车辆实时航向数据绑定(如 vehicleHeading),通过 HeadingPitchRollQuaternion 动态更新模型朝向。

2.2 组合渲染

多实体组合渲染

组件拆解

实体类型技术实现关键属性 / 配置
车辆模型viewer.entities.add + model 属性uri: "/glb/8jc.glb"scale: 1
标签文本label 实体红色填充 + 白色描边,pixelOffset: [0, -30]
扩散波纹3 层 ellipse 实体,通过 CallbackProperty 动态更新半径与透明度semiMajorAxis/semiMinorAxis 随时间线性增长

波纹动画核心代码

for (let i = 0; i < waveCount; i++) {const startTime = Date.now() + i * waveInterval; // 每层波纹延迟1秒启动const waveEntity = viewer.entities.add({position: targetPosition,ellipse: {semiMajorAxis: new Cesium.CallbackProperty(() => {const elapsed = (Date.now() - startTime) % (waveCount * waveInterval);return 10 + 2000 * (elapsed / (waveCount * waveInterval)); // 从10米扩展到2010米}, false),material: new Cesium.ColorMaterialProperty(new Cesium.CallbackProperty(() => {const alpha = 1.0 - (Date.now() - startTime) / (waveCount * waveInterval);return Cesium.Color.RED.withAlpha(Math.max(0, alpha)); // 透明度线性衰减}, false))}});
}

2.3 显隐状态

显隐状态统一管理

toggleVehicleShowStatus() {if (!this.vehicleEntities || this.vehicleEntities.length === 0) return;this.vehicleVisible = !this.vehicleVisible;// 批量更新所有实体状态this.vehicleEntities.forEach(entity => {entity.show = this.vehicleVisible;});
}
  • 设计模式:采用「实体数组管理」模式,将模型、标签、波纹实体存入 vehicleEntities 数组,通过统一接口批量操作显隐状态,避免重复代码。
  • 性能优化:隐藏时自动停止 CallbackProperty 的动态计算,减少无用渲染消耗。

2.4 模型文件

场景中加载的3D车辆模型文件,glb格式文件,见文章顶部。


三、雷达动画

雷达动画开发与交互逻辑

雷达动画通过 HTML DOM 元素与 CSS3 关键帧动画实现,具备轻量化、高性能的特点,可动态锚定地图坐标并响应用户交互。以下是基于代码的详细解析:

3.1 创建元素

基于 JavaScript 创建雷达容器与动画元素

实现方式:通过 document.createElement 动态创建 HTML 元素,避免直接操作静态 DOM,提升代码动态性与可维护性。

DOM 结构生成逻辑

// 创建雷达容器(圆形定位容器)
const container = document.createElement("div");
container.className = "radar_container"; // 关联CSS类
container.style.width = "200px";
container.style.height = "200px";
container.style.borderRadius = "50%"; // 圆形外观
container.style.position = "absolute"; // 绝对定位,基于地图视口坐标
container.style.zIndex = "999"; // 层级置顶,避免被地图覆盖

信息提示文本

const tip = document.createElement("div");
tip.innerText = `求助点位置:${this.realEventList.begMileageNo} - ${this.realEventList.endMileageNo}`;
tip.style.position = "absolute";
tip.style.bottom = "70%"; // 位于容器顶部
tip.style.left = "50%";
tip.style.transform = "translateX(-50%)"; // 水平居中
tip.style.color = "red";
tip.style.pointerEvents = "none"; // 允许鼠标事件穿透到地图

实现效果,提示文本固定在雷达容器正上方,显示事件位置信息,支持动态数据绑定(如 realEventList)。

雷达波纹元素

for (let i = 0; i < 3; i++) {const wave = document.createElement("div");wave.className = "radar_wave"; // 关联CSS类// 基础样式:圆形边框+阴影wave.style.width = "20px";wave.style.height = "20px";wave.style.border = "1px solid red";wave.style.borderRadius = "50%";wave.style.boxShadow = "inset 0 0 5px 1px red"; // 内阴影增强立体感// 定位与动画设置wave.style.top = "50%";wave.style.left = "50%";wave.style.transformOrigin = "center center"; // 动画中心点wave.style.animation = `radar_wave-animation 3s infinite`; // 引用关键帧动画wave.style.animationDelay = `${i}s`; // 延迟启动(0s/1s/2s),形成层次感
}

3.2 动画解析

CSS 关键帧动画解析

动画定义方式:通过 JavaScript 动态向 head 标签注入 <style> 标签,避免全局样式污染。

if (!document.getElementById("radar-wave-style")) {const style = document.createElement("style");style.id = "radar-wave-style";style.innerHTML = `@keyframes radar_wave-animation {0% {transform: translate(-50%, -50%) scale(0); /* 初始状态:缩放为0,位于容器中心 */opacity: 1; /* 完全不透明 */}100% {transform: translate(-50%, -50%) scale(6); /* 最终状态:缩放6倍,覆盖容器范围 */opacity: 0; /* 完全透明 */}}`;document.head.appendChild(style);
}

核心参数说明

属性作用描述
transform: scale()控制波纹半径扩展,scale(6) 表示从初始尺寸(20px)扩展至 120px(覆盖 200px 容器)。
opacity实现波纹渐隐效果,避免动画结束后残留视觉痕迹。
animationDelay每层波纹延迟启动,形成 “多层扩散” 的动态层次感(如第 1 层 0s 启动,第 2 层 1s 启动)。

3.3 坐标联动

地图坐标与屏幕坐标联动

toggleRadarShowStatus() {if (this.radarVisible) {// 初始化坐标转换this.radarPosition = Cesium.Cartesian3.fromDegrees(this.radarLonLat.lon, this.radarLonLat.lat);const canvasPos = viewer.scene.cartesianToCanvasCoordinates(this.radarPosition);// 定位DOM元素this.radarOverlayDom.style.left = `${canvasPos.x - 100}px`; // 水平居中(容器宽200px)this.radarOverlayDom.style.top = `${canvasPos.y - 100}px`;  // 垂直居中// 每帧更新位置(监听场景预渲染事件)this.updateRadarPositionHandler = viewer.scene.preRender.addEventListener(() => {const pos = viewer.scene.cartesianToCanvasCoordinates(this.radarPosition);this.radarOverlayDom.style.left = `${pos.x - 100}px`;this.radarOverlayDom.style.top = `${pos.y - 100}px`;});} else {// 隐藏时移除监听if (this.updateRadarPositionHandler) {viewer.scene.preRender.removeEventListener(this.updateRadarPositionHandler);this.updateRadarPositionHandler = null;}}
}

核心逻辑,通过 cartesianToCanvasCoordinates 将 Cesium 三维坐标转换为浏览器视口坐标,确保雷达容器始终锚定在地图指定位置,支持地图缩放、平移时的实时跟随。

3.4 交互事件

交互事件与信息展示

// 点击雷达触发事件弹窗
container.addEventListener("click", () => {this.EventDialogVisible = true; // 显示Vue弹窗组件
});// 提示文本动态更新(示例)
const tip = document.createElement("div");
tip.innerText = `求助点坐标:${this.realEventList.begMileageNo} - ${this.realEventList.endMileageNo}`;

交互扩展,可结合 Vue/React 等前端框架实现弹窗数据绑定,展示事件详情(如救援进度、预计到达时间)。


四、完整代码

4.1 属性参数

            // 雷达位置的经纬度坐标(地理坐标系)radarLonLat: {lon: 117.610995,lat: 34.399076,},// 雷达动画显示状态控制radarVisible: false,// 雷达动画的DOM容器引用(用于操作HTML元素)radarOverlayDom: null,// 雷达在Cesium中的三维坐标(笛卡尔坐标系)radarPosition: null,// 雷达位置更新事件句柄(用于绑定/移除渲染循环监听)updateRadarPositionHandler: null,// 实际事件数据(存储救援相关信息)// 救援车辆的初始经纬度坐标(地理坐标系)RealCarLonLat: {lon: 117.617227,lat: 34.324461,},

4.2 逻辑代码

加载车辆动画
        //⭐ 加载车辆动画,默认隐藏loadVehicleModelAnime() {// 获取初始经纬度坐标const initLng = this.RealCarLonLat.lon;const initLat = this.RealCarLonLat.lat;// 车辆移动距离(米)const moveDistance = 20;// 地球半径(米)const earthRadius = 6371000;// 初始朝向(0表示正北方向)const heading = 0;// 计算经纬度变化量(基于球面几何)let deltaLat = (moveDistance / earthRadius) * Math.cos(heading);let deltaLng = (moveDistance / (earthRadius * Math.cos(this.Cesium.Math.toRadians(initLat)))) * Math.sin(heading);// 转换为度数let newLat = initLat + this.Cesium.Math.toDegrees(deltaLat);let newLng = initLng + this.Cesium.Math.toDegrees(deltaLng);// 创建目标位置(Cartesian3坐标)const targetPosition = this.Cesium.Cartesian3.fromDegrees(newLng, newLat, 0);// 初始化车辆实体数组this.vehicleEntities = [];// 初始状态为隐藏this.vehicleVisible = false;// 模型实体 - 加载3D车辆模型const vehicleModel = this.viewer.entities.add({name: "车辆模型",position: targetPosition,model: {uri: "/glb/8jc.glb", // 模型文件路径scale: 1, // 初始缩放比例minimumPixelSize: 20, // 最小像素尺寸maximumScale: 40, // 最大缩放比例},// 设置模型朝向(航向角、俯仰角、滚转角)orientation: this.Cesium.Transforms.headingPitchRollQuaternion(targetPosition,new this.Cesium.HeadingPitchRoll(this.Cesium.Math.toRadians(-170), // 航向角(-170度)this.Cesium.Math.toRadians(0),    // 俯仰角(0度)this.Cesium.Math.toRadians(0)     // 滚转角(0度))),show: false // 初始不显示});this.vehicleEntities.push(vehicleModel);// 标签实体 - 显示车辆名称const labelEntity = this.viewer.entities.add({position: targetPosition,label: {text: "拯救车", // 标签文字font: "14px sans-serif", // 字体样式fillColor: this.Cesium.Color.RED, // 文字颜色outlineColor: this.Cesium.Color.WHITE, // 文字轮廓颜色outlineWidth: 1, // 轮廓宽度style: this.Cesium.LabelStyle.FILL_AND_OUTLINE, // 填充加轮廓样式verticalOrigin: this.Cesium.VerticalOrigin.BOTTOM, // 文字垂直对齐底部pixelOffset: new this.Cesium.Cartesian2(0, -30), // 文字偏移量(向上偏移30像素)disableDepthTestDistance: Number.POSITIVE_INFINITY // 禁用深度测试},show: false // 初始不显示});this.vehicleEntities.push(labelEntity);// 动态扩散波纹效果 - 创建多个波纹实体const waveCount = 3; // 波纹数量const waveInterval = 1000; // 波纹生成间隔(毫秒)for (let i = 0; i < waveCount; i++) {// 计算每个波纹的开始时间const startTime = Date.now() + i * waveInterval;const waveEntity = this.viewer.entities.add({position: targetPosition,ellipse: {// 动态椭圆长轴(随时间变化)semiMajorAxis: new this.Cesium.CallbackProperty(() => {let elapsed = (Date.now() - startTime) % (waveCount * waveInterval);let scale = elapsed / (waveCount * waveInterval);return 10 + 2000 * scale; // 从10米逐渐扩大到2010米}, false),// 动态椭圆短轴(随时间变化)semiMinorAxis: new this.Cesium.CallbackProperty(() => {let elapsed = (Date.now() - startTime) % (waveCount * waveInterval);let scale = elapsed / (waveCount * waveInterval);return 10 + 2000 * scale; // 从10米逐渐扩大到2010米}, false),// 动态颜色(透明度随时间变化)material: new this.Cesium.ColorMaterialProperty(new this.Cesium.CallbackProperty(() => {let elapsed = (Date.now() - startTime) % (waveCount * waveInterval);let alpha = 1.0 - elapsed / (waveCount * waveInterval); // 从完全不透明到完全透明return this.Cesium.Color.RED.withAlpha(alpha); // 红色渐变}, false)),height: 0, // 高度为0(贴地)outline: false, // 无轮廓classificationType: this.Cesium.ClassificationType.BOTH, // 贴地显示},show: false // 初始不显示});this.vehicleEntities.push(waveEntity);}},
控制车辆动画
        // 控制车辆动画显示/隐藏状态toggleVehicleShowStatus() {// 如果实体数组不存在或为空,则直接返回if (!this.vehicleEntities || this.vehicleEntities.length === 0) return;// 切换显示状态this.vehicleVisible = !this.vehicleVisible;// 遍历所有车辆相关实体,统一设置显示状态this.vehicleEntities.forEach(entity => {entity.show = this.vehicleVisible;});},

这里的 loadVehicleModelAnime函数 用来创建车辆动画,默认隐藏;toggleVehicleShowStatus函数 用来控制车辆动画,首次执行,显示车辆动画,再次执行,隐藏车辆动画,循环往复。

加载雷达动画
        //⭐ 创建雷达动画,默认隐藏loadRadarLocationAnime() {// 如果雷达DOM已存在,则直接返回(避免重复创建)if (this.radarOverlayDom) return;// 创建雷达容器div(圆形居中布局)const container = document.createElement("div");container.className = "radar_container";// 设置样式container.style.position = "absolute"; // 绝对定位container.style.width = "200px"; // 宽度container.style.height = "200px"; // 高度container.style.borderRadius = "50%"; // 圆形container.style.overflow = "visible"; // 允许内容溢出(显示提示文字)container.style.pointerEvents = "auto"; // 允许鼠标事件container.style.zIndex = "999"; // 置于顶层container.style.display = "flex"; // 弹性布局container.style.alignItems = "center"; // 垂直居中container.style.justifyContent = "center"; // 水平居中container.style.cursor = "pointer"; // 鼠标悬停显示手型container.style.margin = "auto"; // 自动外边距container.style.border = "2px solid transparent"; // 默认透明边框// 创建提示文字元素const tip = document.createElement("div");tip.innerText = `求助点位置:${this.realEventList.begMileageNo} - ${this.realEventList.endMileageNo}`; // 显示求助点位置信息tip.style.textShadow = "2px 3px 1px black"; // 文字阴影效果tip.style.position = "absolute"; // 绝对定位tip.style.bottom = "70%"; // 放在容器底部上方tip.style.left = "50%"; // 水平居中tip.style.transform = "translateX(-50%)"; // 水平居中修正tip.style.padding = "2px 6px"; // 内边距tip.style.font = "14pt"; // 字体大小tip.style.color = "red"; // 文字颜色tip.style.borderRadius = "4px"; // 圆角边框tip.style.whiteSpace = "nowrap"; // 禁止换行tip.style.pointerEvents = "none"; // 不影响鼠标事件tip.style.userSelect = "none"; // 禁止文字选中tip.style.opacity = "1"; // 初始完全不透明tip.style.transition = "opacity 0.3s ease"; // 透明度过渡效果container.appendChild(tip); // 将提示文字添加到容器中// 添加点击事件(点击雷达显示事件详情对话框)container.addEventListener("click", () => {this.EventDialogVisible = true;});// 添加CSS关键帧动画样式(页面只添加一次)if (!document.getElementById("radar-wave-style")) {const style = document.createElement("style");style.id = "radar-wave-style";style.innerHTML = `@keyframes radar_wave-animation {0% {transform: translate(-50%, -50%) scale(0); // 初始状态:缩小到0opacity: 1; // 完全不透明}100% {transform: translate(-50%, -50%) scale(6); // 最终状态:放大6倍opacity: 0; // 完全透明}}`;document.head.appendChild(style); // 将样式添加到head中}// 创建3个雷达波纹div(依次延迟形成波纹效果)for (let i = 0; i < 3; i++) {const wave = document.createElement("div");wave.className = "radar_wave";// 设置波纹样式wave.style.position = "absolute"; // 绝对定位wave.style.width = "20px"; // 宽度wave.style.height = "20px"; // 高度wave.style.border = "1px solid red"; // 红色边框wave.style.borderRadius = "50%"; // 圆形wave.style.boxShadow = "inset 0 0 5px 1px red"; // 内阴影效果wave.style.top = "50%"; // 垂直居中wave.style.left = "50%"; // 水平居中wave.style.transformOrigin = "center center"; // 变换中心点wave.style.transform = "translate(-50%, -50%) scale(0)"; // 初始缩小到0wave.style.animation = `radar_wave-animation 3s infinite`; // 应用动画wave.style.animationDelay = `${i}s`; // 依次延迟(0s, 1s, 2s)container.appendChild(wave); // 将波纹添加到容器中}this.radarOverlayDom = container; // 缓存雷达DOMthis.viewer.container.appendChild(container); // 将雷达添加到Cesium容器中},
控制雷达动画
        // 控制雷达动画显示/隐藏状态toggleRadarShowStatus() {// 如果雷达DOM不存在,则先创建if (!this.radarOverlayDom) {this.loadRadarLocationAnime();}// 切换显示状态this.radarVisible = !this.radarVisible;if (this.radarVisible) {// 计算雷达对应的Cesium笛卡尔坐标(缓存)this.radarPosition = Cesium.Cartesian3.fromDegrees(this.radarLonLat.lon,this.radarLonLat.lat);// 首次设置雷达位置const canvasPos = this.viewer.scene.cartesianToCanvasCoordinates(this.radarPosition);if (canvasPos) {// 计算DOM位置(居中显示)this.radarOverlayDom.style.left = `${canvasPos.x - 100}px`; // 宽度的一半this.radarOverlayDom.style.top = `${canvasPos.y - 100}px`; // 高度的一半this.radarOverlayDom.style.display = "flex"; // 显示雷达}// 注册渲染循环监听(每帧更新雷达位置)this.updateRadarPositionHandler = this.viewer.scene.preRender.addEventListener(() => {const updatedCanvasPos = this.viewer.scene.cartesianToCanvasCoordinates(this.radarPosition);if (updatedCanvasPos) {// 实时更新DOM位置,保持与Cesium场景同步this.radarOverlayDom.style.left = `${updatedCanvasPos.x - 100}px`;this.radarOverlayDom.style.top = `${updatedCanvasPos.y - 100}px`;}});} else {// 隐藏雷达并移除渲染循环监听this.radarOverlayDom.style.display = "none";if (this.updateRadarPositionHandler) {this.updateRadarPositionHandler(); // 移除监听this.updateRadarPositionHandler = null; // 清空引用}}},

这里的 loadRadarLocationAnime函数 用来创建雷达动画,默认隐藏;toggleRadarShowStatus函数 用来控制雷达动画,首次执行,显示雷达动画,再次执行,隐藏雷达动画,循环往复。

4.3 模拟展示

事件组件

// 发送事件通知其他组件需要显示雷达和车辆动画
// 参数1: true 表示需要显示动画
// 参数2: this.realEventList 当前事件数据(如求助点位置等信息)
this.$bus.$emit('brotherEvent1', true, this.realEventList);// 发送事件通知其他组件需要隐藏雷达和车辆动画
// 参数1: false 表示需要隐藏动画
// 参数2: 未传递(此时参数2为undefined)
this.$bus.$emit('brotherEvent1', false);

地图组件

        // 事件推送和动画显示// 监听名为 'brotherEvent1' 的事件(来自兄弟组件或其他模块)this.$bus.$on('brotherEvent1', (res, data) => {// 如果事件返回 true,表示需要显示雷达和车辆动画if (res) {// 立即更新事件数据(不需要延迟)this.realEventList = data;// 延迟 1 秒后执行以下操作(确保 DOM 已更新)setTimeout(() => {// 加载雷达动画(如果尚未加载)this.loadRadarLocationAnime();// 显示雷达动画this.toggleRadarShowStatus();// 加载车辆模型动画(如果尚未加载)this.loadVehicleModelAnime();// 显示车辆模型动画this.toggleVehicleShowStatus();}, 1000); // 延迟 1 秒执行// 如果事件返回 false,表示需要隐藏雷达和车辆动画} else if (res === false) {// 隐藏雷达动画this.toggleRadarShowStatus();// 隐藏车辆模型动画this.toggleVehicleShowStatus();}});

这里通过事件组件的 WebSocket 推流数据状态和 EventBus 通信控制动画显示/隐藏:当收到 brotherEvent1(true, data) 时,更新事件数据并延迟1秒加载雷达和车辆动画;收到 brotherEvent1(false) 时直接隐藏动画;根据需求自行更改。


五、注意事项

注意事项与优化建议

5.1 资源加载与路径问题

  • 模型路径:确保 GLB 文件路径正确(如 uri: "/static/glb/8jc.glb"),避免跨域问题(可配置服务器 CORS 或使用相对路径)。本文代码里的 uri: "/glb/8jc.glb", 模型文件是放在public文件下的。

5.2 性能与内存管理

  1. 实体清理:使用 viewer.entities.removeAll() 销毁不再需要的车辆实体,避免内存泄漏。
  2. 动画频率:雷达波纹 waveInterval 建议设为 500-1000ms,过短会导致 CPU 占用过高;车辆波纹 waveCount 不建议超过 5 层。

5.3 兼容性与调试

  1. 浏览器支持:Cesium 依赖 WebGL 2.0,需确保用户浏览器支持(可添加 webgl-lint 库进行检测)。
  2. 坐标调试:通过 console.log(canvasPos) 打印屏幕坐标,辅助定位雷达偏移问题;使用 Cesium 调试工具 viewer.scene.debugShowFramesPerSecond = true 监控帧率变化。


六、本文总结

本文总结与扩展方向

扩展方向

本文通过 Cesium 实体系统与 HTML 动画结合,实现了车辆与雷达的动态交互效果。实际项目中可进一步扩展:

  1. 数据驱动:接入 WebSocket 实时更新车辆位置与雷达事件数据。
  2. 视觉增强:为车辆模型添加灯光效果(ModelStyle),雷达波纹支持多颜色渐变。
  3. 交互升级:实现鼠标悬停时车辆信息弹窗、雷达波纹点击缩放等高级功能。

延伸阅读

  • Cesium 7 原文链接(含高速道路完整代码)
  • Cesium 官方模型加载文档
  • CSS3 动画性能优化指南

通过两篇文章的结合,可构建从 “基础场景→动态交互→业务逻辑” 的完整 Cesium 可视化能力,适用于智慧交通、数字孪生等复杂场景开发。

本文围绕 Cesium 中动态元素的核心需求,详细讲解了车辆模型与雷达动画的完整实现流程,涵盖坐标计算、实体渲染、交互控制及性能优化。结合前篇道路场景开发,可构建 “静态场景 + 动态交互” 的完整可视化方案,适用于智慧交通、应急救援等领域。完整项目代码可通过留言、私信作者获取,欢迎一起交流探讨 Cesium 开发技巧!


七、更多操作

请看,CesiumJS Develop 个人专栏

CesiumJShttps://blog.csdn.net/weixin_65793170/category_12356611.htmlhttps://blog.csdn.net/weixin_65793170/category_12356611.html


http://www.hkcw.cn/article/ZEBiUvJRqo.shtml

相关文章

ElasticSearch简介及常用操作指南

一. ElasticSearch简介 ElasticSearch 是一个基于 Lucene 构建的开源、分布式、RESTful 风格的搜索和分析引擎。 1. 核心功能 强大的搜索能力 它能够提供全文检索功能。例如&#xff0c;在海量的文档数据中&#xff0c;可以快速准确地查找到包含特定关键词的文档。这在处理诸如…

Transformer《Attention is all you need》

发布时间&#xff1a;2017/06/12 发布单位&#xff1a;Google、多伦多⼤学 简单摘要&#xff1a;直译为“变换器”&#xff0c;⼀种采⽤⾃注意⼒机制的深度学习模型&#xff0c;按照输⼊数据各部分重要 性不同⽽分配不同权重。⼴泛⽤于NLP和CV领域。 阅读重点&#xff1a;s…

html+css+js趣味小游戏~HexGL赛车竞速(附源码)

下面是一个简单的记忆卡片配对游戏的完整代码&#xff0c;使用HTML、CSS和JavaScript实现&#xff1a; html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…

VL 中间语言核心技术架构:构建全链路开发生态

一、VL 中间语言核心架构&#xff1a;全链路开发的三大关键层级 在企业级应用开发面临效率与技术深度双重挑战的背景下&#xff0c;iVX 自主研发的 VL&#xff08;Visual Language&#xff09;中间语言体系&#xff0c;通过 "可视化建模 - 语义编译 - 多端适配" 三大…

GPU 图形计算综述 (二):固定管线

在计算机图形学中&#xff0c;图形管线&#xff08;Graphics Pipeline&#xff09;是指通过一系列软硬件算法&#xff0c;将三维空间中的物体表征&#xff0c;转换为二维空间的物体表征的过程。一般通过3D网格&#xff08;Mesh&#xff09;等图元&#xff08;Primitive&#xf…

Manus数据手套:赋能人形机器人遥操作与AI数据训练的创新力量

人形机器人技术与AI技术正在进入蓬勃发展的黄金时代。特斯拉高调发布其即将推向市场的人形机器人Optimus&#xff0c;引发全球瞩目&#xff1b;与此同时&#xff0c;国内人形机器人产业也如雨后春笋般迅速崛起&#xff0c;展现出强劲的发展势头。在这一技术浪潮中&#xff0c;M…

C# 控制台程序实现定时自动退出

一、基础实现方式&#xff1a;同步阻塞等待 通过Thread.Sleep暂停主线程&#xff0c;适合简单场景&#xff08;需阻塞当前线程&#xff09;。 static void Main(string[] args) { Console.WriteLine("程序启动&#xff0c;5秒后自动退出..."); Thread.Slee…

【笔记】suna部署之获取 Firecrawl API key

#工作记录 Firecrawl 一、前期准备 在进行 Suna 部署时&#xff0c;获取 Firecrawl API key 是其中一个关键步骤。Firecrawl 是一款功能强大的工具&#xff0c;在 Suna 项目中可发挥重要作用&#xff0c;比如助力数据获取等相关任务。 二、获取步骤 &#xff08;一&#xff…

花哨桌面 V 3.0.0 (火影忍者版)

废话不多说,直接上链接,源码在之前版本的帖子里,本次主要修改了部分元素. 功能也不描述了哦 效果图

西门子PLC结构化编程_优化后的调节阀标准块

文章目录 前言一、功能概述二、程序编写1.新建数据类型“5_RegvalveType”2.新建FB块“6_Regvalve”3.SCL和LAD混合编程 总结 前言 在之前的文章中&#xff0c;分享过一个基于SCL语言实现的调节阀控制块西门子PLC常用底层逻辑块分享_调节阀&#xff0c;在实际应用过程中&#…

react-color-palette源码解析

项目中用到了react-color-palette组件&#xff0c;以前对第三方组件都是不求甚解&#xff0c;这次想了解一下其实现细节。 简介 react-color-palette 是一个用于创建颜色调色板的 React 组件。它提供了一个简单易用的接口&#xff0c;让开发者可以轻松地创建和管理颜色调色板。…

(一)视觉——工业相机(以海康威视为例)

一、工业相机介绍 工业相机是机器视觉系统中的一个关键组件&#xff0c;其最本质的功能就是将光信号转变成有序的电信号。选择合适的相机也是机器视觉系统设计中的重要环节&#xff0c;相机的选择不仅直接决定所采集到的图像分辨率、图像质量等&#xff0c;同时也与整个系统的运…

PnP(Perspective-n-Point)算法 | 用于求解已知n个3D点及其对应2D投影点的相机位姿

什么是PnP算法&#xff1f; PnP 全称是 Perspective-n-Point&#xff0c;中文叫“n点透视问题”。它的目标是&#xff1a; 已知一些空间中已知3D点的位置&#xff08;世界坐标&#xff09;和它们对应的2D图像像素坐标&#xff0c;求解摄像机的姿态&#xff08;位置和平移&…

C++核心编程_4.5 运算符重载_4.5.1 加号运算符重载

#include <iostream> #include <string> using namespace std;/* ### 4.5 运算符重载 运算符重载概念&#xff1a;对已有的运算符重新进行定义&#xff0c;赋予其另一种功能&#xff0c;以适应不同的数据类型 *//* 4.5.1 加号运算符重载 作用&#xff1a;实现两…

文本预处理

文本预处理 1 词向量表示 1.1 word2vec之skipgram方式&#xff1a; 定义&#xff1a;给你一段文本&#xff0c;选定特定的窗口长度&#xff0c;然后利用中间词来预测上下文 实现过程&#xff1a;1、选定一个窗口长度&#xff1a;3、5、7等&#xff1b;2、指定词向量的维度&a…

C++中单例模式详解

在C中&#xff0c;单例模式 (Singleton Pattern) 确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例。这在需要一个全局对象来协调整个系统行为的场景中非常有用。 为什么要有单例模式&#xff1f; 在许多项目中&#xff0c;某些类从逻辑上讲只需要一个实…

什么是单片机?

众所周知&#xff0c;人类行为受大脑调控&#xff0c;正如视觉、听觉、味觉、嗅觉、触觉及运动功能等感官与肢体活动均受其指挥&#xff1b;换言之&#xff0c;大脑作为人体的中枢神经系统&#xff0c;负责管理所有可控制的生理功能。 在电子设备领域&#xff0c;单片机…

DMBOK对比知识点整理(4)

1.常见数据质量维度 常见数据质量维度(DMBOK-P353)质量维度

Web攻防-SQL注入增删改查盲注延时布尔报错有无回显错误处理

知识点&#xff1a; 1、Web攻防-SQL注入-操作方法&增删改查 2、Web攻防-SQL注入-布尔&延时&报错&盲注 案例说明&#xff1a; 在应用中&#xff0c;存在增删改查数据的操作&#xff0c;其中SQL语句结构不一导致注入语句也要针对应用达到兼容执行&#xff0c;另…

动态规划-152.乘积最大子数组-力扣(LeetCode)

一、题目解析 根据示例nums数组中存在负数&#xff0c;下面分析时需注意 二、算法原理 1、状态表示 此时f[i]表示&#xff1a;以i位置为结尾的所有子数组中的最大乘积&#xff0c;但是由于nums中存在负数&#xff0c;所以还需要g[i]表示&#xff1a;以i位置为结尾的所有子数组…