
从零封装一个Cesium动态扩散圆插件支持自定义颜色、速度与图片纹理1. 动态扩散圆技术原理与实现思路动态扩散圆效果是通过不断改变圆形实体的半径和材质透明度实现的。在Cesium中我们可以利用CallbackProperty机制动态更新图形属性这是实现动态效果的关键技术。核心实现原理半径动态变化通过CallbackProperty持续更新椭圆的半长轴和半短轴透明度渐变根据半径变化计算透明度值实现淡入淡出效果材质控制支持纯色填充和图片纹理两种模式1.1 CallbackProperty性能分析Cesium提供了两种属性更新方式更新方式原理性能影响适用场景直接属性更新每帧手动修改entity属性需要手动管理性能较差简单动画更新频率低CallbackProperty回调函数动态计算属性值内置优化性能较好连续动态效果// 性能对比示例 // 方式一直接更新不推荐 setInterval(() { entity.ellipse.semiMajorAxis newRadius; }, 16); // 方式二CallbackProperty推荐 entity.ellipse.semiMajorAxis new Cesium.CallbackProperty(() { return newRadius; }, false);提示对于需要频繁更新的动态效果始终优先使用CallbackProperty。它的isConstant参数设为false时Cesium会每帧调用回调函数获取最新值。2. 插件设计与封装2.1 类结构设计/** * 动态扩散圆插件 * class DynamicRange * param {Object} options 配置项 * param {Cesium.Viewer} options.viewer Cesium Viewer实例 */ class DynamicRange { constructor(options) { this.viewer options.viewer; this._entities []; } /** * 创建纹理材质扩散圆 * param {Object} options 配置项 * returns {Cesium.Entity} 创建的实体 */ createTextured(options) { // 实现代码... } /** * 创建纯色材质扩散圆 * param {Object} options 配置项 * returns {Cesium.Entity} 创建的实体 */ createSolidColor(options) { // 实现代码... } /** * 移除所有扩散圆 */ removeAll() { // 实现代码... } }2.2 配置参数说明参数名类型默认值说明positionCartesian3必填圆心位置(笛卡尔坐标)minRadiusnumber4000最小半径(米)maxRadiusnumber400000最大半径(米)speednumber4000扩散速度(米/秒)colorColorColor.WHITE基础颜色imageUrlstringnull纹理图片URLoutlineColorColorColor.RED轮廓线颜色outlineWidthnumber1.0轮廓线宽度3. 完整实现代码/** * 动态扩散圆插件 */ export default class DynamicRange { constructor(options) { if (!options.viewer) { throw new Error(必须提供Cesium Viewer实例); } this.viewer options.viewer; this._entities []; } /** * 创建动态扩散圆 * param {Object} options 配置参数 * returns {Cesium.Entity} 创建的实体 */ create(options) { const { position, minRadius 4000, maxRadius 400000, speed 4000, color Cesium.Color.WHITE, imageUrl null, outlineColor Cesium.Color.RED, outlineWidth 1.0 } options; if (!position) { throw new Error(必须指定圆心位置); } // 当前半径状态 let currentRadius minRadius; const deviationR speed; // 半径变化函数 const changeRadius () { currentRadius deviationR; if (currentRadius maxRadius) { currentRadius minRadius; } return currentRadius; }; // 颜色变化函数 const changeColor () { const alpha 1 - (currentRadius / maxRadius); return color.withAlpha(alpha); }; // 创建材质 let material; if (imageUrl) { material new Cesium.ImageMaterialProperty({ image: imageUrl, repeat: new Cesium.Cartesian2(1.0, 1.0), transparent: true, color: new Cesium.CallbackProperty(changeColor, false) }); } else { material new Cesium.ColorMaterialProperty( new Cesium.CallbackProperty(changeColor, false) ); } // 创建实体 const entity this.viewer.entities.add({ position: position, ellipse: { semiMinorAxis: new Cesium.CallbackProperty(changeRadius, false), semiMajorAxis: new Cesium.CallbackProperty(changeRadius, false), material: material, outline: true, outlineColor: outlineColor, outlineWidth: outlineWidth } }); this._entities.push(entity); return entity; } /** * 移除所有创建的扩散圆 */ removeAll() { this._entities.forEach(entity { this.viewer.entities.remove(entity); }); this._entities []; } }4. 使用示例4.1 基本使用// 初始化插件 const dynamicRange new DynamicRange({ viewer: viewer }); // 创建纯色扩散圆 const solidCircle dynamicRange.create({ position: Cesium.Cartesian3.fromDegrees(116.4, 39.9), minRadius: 1000, maxRadius: 5000, speed: 50, color: Cesium.Color.BLUE.withAlpha(0.7), outlineColor: Cesium.Color.WHITE }); // 创建纹理扩散圆 const texturedCircle dynamicRange.create({ position: Cesium.Cartesian3.fromDegrees(116.41, 39.91), minRadius: 1500, maxRadius: 6000, speed: 80, imageUrl: path/to/texture.png }); // 移除所有扩散圆 // dynamicRange.removeAll();4.2 Vue3组件封装示例// DynamicRange.vue template div/div /template script import { onMounted, onBeforeUnmount } from vue; import DynamicRange from ./DynamicRange; export default { props: { viewer: { type: Object, required: true }, circles: { type: Array, default: () [] } }, setup(props) { let dynamicRange; onMounted(() { dynamicRange new DynamicRange({ viewer: props.viewer }); props.circles.forEach(circle { dynamicRange.create(circle); }); }); onBeforeUnmount(() { dynamicRange?.removeAll(); }); return {}; } }; /script5. 性能优化建议纹理优化使用适当尺寸的纹理图片推荐256x256或512x512预加载纹理图片避免运行时加载延迟实例管理及时移除不再需要的扩散圆对大量扩散圆使用Primitive API替代Entity API回调函数优化确保回调函数执行效率高避免复杂计算对于固定速度的动画可以使用时间戳计算而非累加// 基于时间的半径计算更精确的速度控制 const startTime Cesium.JulianDate.now(); const changeRadius () { const seconds Cesium.JulianDate.secondsDifference( Cesium.JulianDate.now(), startTime ); const progress (seconds * speed) % (maxRadius - minRadius); return minRadius progress; };6. 扩展功能实现6.1 支持动画完成回调// 修改后的create方法 create(options) { // ...原有代码... let onComplete options.onComplete; const changeRadius () { currentRadius deviationR; if (currentRadius maxRadius) { currentRadius minRadius; onComplete?.(); } return currentRadius; }; // ...其余代码... }6.2 多圆同步动画控制class DynamicRange { constructor(options) { // ...原有代码... this._animationStart Cesium.JulianDate.now(); } create(options) { // ...原有代码... // 基于统一时间计算半径 const changeRadius () { const seconds Cesium.JulianDate.secondsDifference( Cesium.JulianDate.now(), this._animationStart ); const progress (seconds * speed) % (maxRadius - minRadius); return minRadius progress; }; // ...其余代码... } // 重置所有动画 resetAnimation() { this._animationStart Cesium.JulianDate.now(); } }7. 实际应用案例7.1 应急响应范围可视化// 创建多个不同颜色的扩散圆表示不同应急级别 const emergencyLevels [ { color: Cesium.Color.RED, radius: 5000 }, { color: Cesium.Color.YELLOW, radius: 3000 }, { color: Cesium.Color.GREEN, radius: 1000 } ]; emergencyLevels.forEach((level, index) { dynamicRange.create({ position: incidentLocation, minRadius: 500, maxRadius: level.radius, speed: 100 (index * 20), color: level.color.withAlpha(0.5), outlineColor: level.color }); });7.2 雷达扫描效果// 创建扇形雷达扫描效果 const radarSector dynamicRange.create({ position: radarPosition, minRadius: 1000, maxRadius: 10000, speed: 200, imageUrl: path/to/radar-sector.png, outlineColor: Cesium.Color.CYAN }); // 调整实体旋转 radarSector.ellipse.rotation new Cesium.CallbackProperty(() { const now Cesium.JulianDate.now(); const seconds Cesium.JulianDate.secondsDifference(now, startTime); return Cesium.Math.toRadians(seconds * 10); }, false);注意实际项目中建议将纹理图片转换为Base64编码或使用Cesium的Resource预加载机制确保纹理能够正确加载显示。