vue + Cesium 实现静态波纹点效果
在地理信息可视化领域,动态效果往往能让数据展示更加生动直观。本文将详细解析如何使用 Cesium.js 实现一个静态波纹点效果,这种效果常用于标记重要地理位置、热力中心或事件发生点,能够有效吸引用户注意力并突出关键信息。
效果展示与应用场景
静态波纹点效果是指在固定位置产生向外扩散的波纹动画,通常由多个同心圆组成,这些同心圆会周期性地从中心向外扩展并逐渐消失,形成类似水波扩散的视觉效果。
这种效果在以下场景中特别有用:
- 重要 POI(兴趣点)标记,如地标建筑、交通枢纽
- 实时数据监测点,如气象站、环境监测设备
- 事件发生地标记,如灾害发生点、事故现场
- 网络覆盖范围动态展示
实现原理分析
实现波纹点效果的核心原理基于以下几点:
- 使用 Cesium 的Ellipse实体绘制圆形波纹
- 通过CallbackProperty实现波纹的动态更新
- 利用三角函数计算波纹的半径和透明度变化
- 多个波纹实体通过相位差实现连续动画效果
完整代码解析
下面我们来详细解析实现静态波纹点效果的完整代码:
组件结构
import initMap from \'@/config/initMap.js\';import { mapConfig } from \'@/config/mapConfig\';export default {data() {return {viewer: null,};},// ... 其余代码省略};#cesiumContainer {width: 100%;height: 100vh;touch-action: none;}
组件采用标准的 Vue 单文件结构,包含三个部分:
- 模板部分:定义了 Cesium 地图容器
- 脚本部分:实现核心逻辑
- 样式部分:设置容器样式,touch-action: none用于优化触摸设备上的交互体验
初始化地图
mounted() {// 初始化地图实例this.viewer = initMap(mapConfig.gaode.url3, false);// 定义天津坐标 (北纬39.13°,东经117.20°)const tianjinCoordinates = Cesium.Cartesian3.fromDegrees(117.2, 39.13, 100);// 创建波纹效果this.createRippleEffect(tianjinCoordinates, // 位置\'#1E90FF\', // 颜色(蓝色)1500, // 最大半径(米)1.2, // 动画速度10 // 波纹数量);}
在组件挂载阶段,我们完成了三件事:
- 初始化 Cesium 地图实例
- 定义了波纹点的位置(这里选择了天津的坐标)
- 调用createRippleEffect方法创建波纹效果
核心函数:createRippleEffect
这个函数是实现波纹效果的核心,我们逐部分解析:
createRippleEffect(position, color, maxRadius, speed, count) {// 创建一个实体集合来管理所有波纹const ripples = this.viewer.entities.add({position: position,name: \'波纹点\',});// 为每个波纹创建一个圆for (let i = 0; i {// 计算当前时间的动画进度const time = this.viewer.clock.currentTime.secondsOfDay;const progress = (time * speed + phase) % (Math.PI * 2);const scale = (1 - Math.cos(progress)) / 2; // 0到1之间变化// 更新半径和透明度const currentRadius = scale * maxRadius;const currentAlpha = 1 - scale; // 随着半径增大而逐渐透明// 更新圆的大小ripple.ellipse.semiMinorAxis = currentRadius;ripple.ellipse.semiMajorAxis = currentRadius;// 返回当前颜色(保持颜色不变,只改变透明度)return Cesium.Color.fromCssColorString(color).withAlpha(currentAlpha);}, false)),outline: false,},});}// 添加一个中心点viewer.entities.add({position: position,point: {pixelSize: 8,color: Cesium.Color.fromCssColorString(color),outlineColor: Cesium.Color.WHITE,outlineWidth: 2}});return ripples;}
1. 波纹相位计算
const phase = (i / count) * Math.PI * 2;
这段代码为每个波纹计算了不同的初始相位,使得多个波纹能够错开动画时间,形成连续不断的扩散效果。如果没有相位差,所有波纹会同步运动,看起来就像一个单一的波纹在放大缩小。
2. 动画进度计算
const time = this.viewer.clock.currentTime.secondsOfDay;const progress = (time * speed + phase) % (Math.PI * 2);const scale = (1 - Math.cos(progress)) / 2;
这是动画的核心计算部分:
- 使用 Cesium 的时钟获取当前时间
- 结合速度和相位计算进度值
- 利用余弦函数将进度值转换为 0 到 1 之间的缩放比例,实现平滑的周期变化
3. 波纹属性更新
const currentRadius = scale * maxRadius;const currentAlpha = 1 - scale;
根据计算出的缩放比例,更新波纹的两个关键属性:
- 半径:随着 scale 从 0 增加到 1,半径从 0 增加到 maxRadius
- 透明度:随着 scale 从 0 增加到 1,透明度从 1 降低到 0,实现波纹逐渐消失的效果
4. 中心点添加
为了增强视觉效果,代码在波纹中心添加了一个固定的点,使整个效果更加完整。
关键技术点详解
CallbackProperty 的应用
Cesium 中的CallbackProperty是实现动态效果的关键类,它允许我们定义一个回调函数,该函数会在每次渲染时被调用,返回最新的属性值。
在本效果中,CallbackProperty被用于动态更新波纹的颜色(主要是透明度),而波纹的大小则通过直接修改椭圆的半轴长度来实现。
性能优化考量
- 实体数量控制:虽然代码中使用了 10 个波纹,但在实际应用中应根据需求调整。过多的波纹会增加渲染负担,特别是在同时展示多个波纹点时。
- 材质复用:如果多个波纹点使用相同的颜色,可以考虑复用材质对象,减少内存占用。
- 视距优化:可以添加视距判断,当距离过远时减少波纹数量或停止动画,提升性能。
代码优化与扩展建议
1. 增加参数配置
可以将波纹效果的各项参数封装成一个配置对象,使代码更具可维护性和复用性:
const rippleOptions = {color: \'#1E90FF\',maxRadius: 1500,speed: 1.2,count: 10,centerPointSize: 8,// 可以添加更多配置项};
2. 实现波纹点管理
添加波纹点的创建、删除、更新方法,方便在复杂场景中管理多个波纹点:
// 存储所有波纹点实体data() {return {viewer: null,rippleEntities: []};},methods: {// 添加波纹点addRipplePoint(position, options) {const ripple = this.createRippleEffect(position, ...);this.rippleEntities.push(ripple);return ripple;},// 移除所有波纹点removeAllRipples() {this.rippleEntities.forEach(entity => {this.viewer.entities.remove(entity);});this.rippleEntities = [];}}
3. 响应式参数调整
结合 UI 控件实现波纹参数的实时调整,提升交互体验:
4. 增加点击添加功能
允许用户在地图上点击添加波纹点:
mounted() {// ... 初始化代码// 监听地图点击事件this.viewer.screenSpaceEventHandler.setInputAction(event => {const position = this.viewer.camera.pickEllipsoid(event.position,this.viewer.scene.globe.ellipsoid);if (position) {this.createRippleEffect(position, \'#1E90FF\', 1500, 1.2, 10);}}, Cesium.ScreenSpaceEventType.LEFT_CLICK);}
基础代码
import initMap from \'@/config/initMap.js\';import { mapConfig } from \'@/config/mapConfig\';export default { data() { return { viewer: null, }; }, mounted() { this.viewer = initMap(mapConfig.gaode.url3, false); // 天津坐标 (北纬39.13°,东经117.20°) const tianjinCoordinates = Cesium.Cartesian3.fromDegrees(117.2, 39.13, 100); // 在天津位置创建波纹效果 this.createRippleEffect( tianjinCoordinates, // 位置 \'red\', // 颜色(蓝色) 1500, // 最大半径(米) 1.2, // 动画速度 10 // 波纹数量 ); }, methods: { // 创建静态波纹点效果的函数 createRippleEffect(position, color, maxRadius, speed, count) { // 创建一个实体集合来管理所有波纹 const ripples = this.viewer.entities.add({ position: position, name: \'波纹点\', }); // 为每个波纹创建一个圆 for (let i = 0; i { // 计算当前时间的动画进度 const time = this.viewer.clock.currentTime.secondsOfDay; const progress = (time * speed + phase) % (Math.PI * 2); const scale = (1 - Math.cos(progress)) / 2; // 0到1之间变化 // 更新半径和透明度 const currentRadius = scale * maxRadius; const currentAlpha = 1 - scale; // 随着半径增大而逐渐透明 // 更新圆的大小 ripple.ellipse.semiMinorAxis = currentRadius; ripple.ellipse.semiMajorAxis = currentRadius; // 返回当前颜色(保持颜色不变,只改变透明度) return Cesium.Color.fromCssColorString(color).withAlpha( currentAlpha ); }, false) ), outline: false, }, }); } // 添加一个中心点 this.viewer.entities.add({ position: position, point: { pixelSize: 8, color: Cesium.Color.fromCssColorString(color), outlineColor: Cesium.Color.WHITE, outlineWidth: 2, }, }); return ripples; }, }, beforeDestroy() { // 组件销毁时释放资源 if (this.viewer) { this.viewer.destroy(); } },};#cesiumContainer { width: 100%; height: 100vh; touch-action: none;}
常见问题与解决方案
1. 波纹动画不流畅
- 检查波纹数量是否过多,适当减少
- 降低最大半径或调整速度参数
- 确保 Cesium 版本与代码兼容(本文代码基于 1.129 版本测试通过)
2. 波纹形状不规则
- 当地图存在地形时,可能会导致波纹形状变形
- 可以通过提高波纹的高度(Z 值)减少地形影响
- 或使用clampToGround: true属性使波纹贴合地形
3. 性能问题
- 在低性能设备上,可减少波纹数量
- 实现视距相关的动态细节控制
- 考虑使用广告牌(Billboard)配合自定义纹理实现波纹效果
总结
本文详细解析了基于 Cesium 实现静态波纹点效果的完整代码,从核心原理到具体实现,再到优化扩展。这种效果虽然简单,但在地理信息可视化中非常实用。
通过理解波纹相位计算、动画进度控制和 Cesium 实体属性动态更新的原理,开发者可以举一反三,实现更复杂的动态效果,如脉冲效果、扩散波纹组等。
希望本文能帮助大家更好地掌握 Cesium 的动态效果开发技巧,创造出更丰富的地理信息可视化应用。
代码已在 Cesium 1.129 版本测试通过,其他版本可能需要适当调整。如果有任何问题或优化建议,欢迎在评论区交流讨论。