Appearance
别再瞎写 Cesium 特效!扩散墙这样写最流畅,科技感拉满!源码直接拷走复用
接上文,做了个雷达扫描的效果,预警效果的话增加一个扩散墙的效果。
动态扩散效果常用于重点区域预警、电子围栏激活、目标区域提示、应急事件展示等场景,这里基于Cesium原生API,实现圆形和多边形扩散墙两种效果。
两套效果均使用 Wall + CallbackProperty 实现,流畅无卡顿,支持Vue/React/原生JS项目直接接入。
实现方案
两种扩散墙共用一套技术逻辑,仅形状生成方式不同:
使用 Cesium.Wall 绘制垂直墙体,然后通过 CallbackProperty 实时更新墙体顶点位置。
控制半径/缩放值从小到大、高度从大到小,形成扩散动画。
最后达到阈值后重置,实现循环扩散。
实现代码
javascript
/**
* 圆形扩散效果
*/
const createCircleDiffusionEffect = () => {
if (!cesiumViewer.value) return;
if (!isShowCircleDiffusion.value) {
// 创建圆形扩散效果
isShowCircleDiffusion.value = true;
// 定义状态变量
let center = [117.105356, 36.437555];
let radius = 140.0;
let speed = 5.0;
let height = 30.0;
let minRadius = 20.0;
let color = new Cesium.Color(0.0, 0.8, 1.0, 1.0);
let currentHeight = height;
let currentRadius = minRadius;
let entity = null;
// 添加圆形实体
entity = cesiumViewer.value.entities.add({
wall: {
// callbackProperty回调函数,实时更新
positions: new Cesium.CallbackProperty(() => {
let positions = [];
// 更新半径和高度
currentRadius += (radius * speed) / 1000.0;
currentHeight -= (height * speed) / 1000.0;
// 判断扩散的实际半径和高度是否超出范围
if (currentRadius > radius || currentHeight < 0) {
currentRadius = minRadius;
currentHeight = height;
}
// 获取圆形位置点
let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
Cesium.Cartesian3.fromDegrees(center[0], center[1], 0)
);
// 生成圆形顶点
for (let i = 0; i < 64; i++) {
let angle = (i / 64) * Cesium.Math.TWO_PI;
let x = Math.cos(angle);
let y = Math.sin(angle);
let point = new Cesium.Cartesian3(
x * currentRadius,
y * currentRadius,
currentHeight
);
positions.push(
Cesium.Matrix4.multiplyByPoint(
modelMatrix,
point,
new Cesium.Cartesian3()
)
);
}
// 封闭圆形,首节点需要存两次
positions.push(positions[0]);
return positions;
}, false),
// 设置材质 - 使用半透明的颜色
material: Cesium.Color.fromCssColorString('rgba(0, 204, 255, 0.7)')
},
});
// 存储引用
circleDiffusionRef.value = {
remove: () => {
if (entity && cesiumViewer.value) {
cesiumViewer.value.entities.remove(entity);
entity = null;
}
},
update: (options) => {
if (options.center) center = options.center;
if (options.radius) radius = options.radius;
if (options.speed) speed = options.speed;
if (options.height) {
height = options.height;
currentHeight = options.height;
}
if (options.color) {
color = options.color;
if (entity) {
entity.wall.material = color;
}
}
},
entity: entity
};
console.log('✅ 圆形扩散效果已创建');
} else {
// 移除圆形扩散效果
isShowCircleDiffusion.value = false;
if (circleDiffusionRef.value) {
circleDiffusionRef.value.remove();
circleDiffusionRef.value = null;
}
console.log('✅ 圆形扩散效果已移除');
}
// 强制场景更新
if (cesiumViewer.value && cesiumViewer.value.scene) {
cesiumViewer.value.scene.requestRender();
}
}这里材质上我仅仅使用了半透明的纯色,如果有需要我觉得可以上贴图。效果更好!
javascript
/**
* 多边形扩散效果
* */
const createPolygonDiffusionEffect = () => {
if (!cesiumViewer.value) return;
if (!isShowPolygonDiffusion.value) {
// 创建多边形扩散效果
isShowPolygonDiffusion.value = true;
// 定义状态变量
// 使用电子围栏的坐标作为基础多边形
const basePolygon = [
{ lon: 117.105951, lat: 36.437923 },
{ lon: 117.105898, lat: 36.437773 },
{ lon: 117.106451, lat: 36.437648 },
{ lon: 117.106506, lat: 36.437808 },
];
// 计算多边形中心点
const calculatePolygonCenter = (vertices) => {
let sumLon = 0, sumLat = 0;
for (const vertex of vertices) {
sumLon += vertex.lon;
sumLat += vertex.lat;
}
return { lon: sumLon / vertices.length, lat: sumLat / vertices.length };
};
// 计算顶点相对于中心点的偏移量(以米为单位)
const calculateVertexOffsets = (vertices, center) => {
const offsets = [];
for (const vertex of vertices) {
// 将经纬度转换为笛卡尔坐标
const vertexCart = Cesium.Cartesian3.fromDegrees(vertex.lon, vertex.lat, 0);
const centerCart = Cesium.Cartesian3.fromDegrees(center.lon, center.lat, 0);
// 计算偏移向量
const offset = new Cesium.Cartesian3();
Cesium.Cartesian3.subtract(vertexCart, centerCart, offset);
offsets.push(offset);
}
return offsets;
};
const center = calculatePolygonCenter(basePolygon);
const vertexOffsets = calculateVertexOffsets(basePolygon, center);
let maxScale = 5; // 最大缩放比例
let speed = 5; // 扩散速度
let height = 30.0; // 初始高度
let minScale = 1.0; // 最小缩放比例
let color = new Cesium.Color(1.0, 0.2, 0.2, 1.0); // 亮红色
let currentHeight = height;
let currentScale = minScale;
let entity = null;
// 添加多边形实体
entity = cesiumViewer.value.entities.add({
wall: {
// callbackProperty回调函数,实时更新
positions: new Cesium.CallbackProperty(() => {
let positions = [];
// 更新缩放比例和高度
currentScale += (maxScale * speed) / 1000.0;
currentHeight -= (height * speed) / 1000.0;
// 判断扩散的实际缩放比例和高度是否超出范围
if (currentScale > maxScale || currentHeight < 0) {
currentScale = minScale;
currentHeight = height;
}
// 生成扩散后的多边形顶点
for (let i = 0; i < vertexOffsets.length; i++) {
const offset = vertexOffsets[i];
// 计算缩放后的偏移量
const scaledOffset = new Cesium.Cartesian3();
Cesium.Cartesian3.multiplyByScalar(offset, currentScale, scaledOffset);
// 计算最终位置(中心点 + 缩放后的偏移量)
const centerCart = Cesium.Cartesian3.fromDegrees(center.lon, center.lat, currentHeight);
const position = new Cesium.Cartesian3();
Cesium.Cartesian3.add(centerCart, scaledOffset, position);
positions.push(position);
}
// 封闭多边形,首节点需要存两次
positions.push(positions[0]);
return positions;
}, false),
// 设置材质 - 使用亮红色
material: Cesium.Color.fromCssColorString('rgba(255, 0, 0, 0.7)')
},
});
// 存储引用
polygonDiffusionRef.value = {
remove: () => {
if (entity && cesiumViewer.value) {
cesiumViewer.value.entities.remove(entity);
entity = null;
}
},
update: (options) => {
if (options.maxScale) maxScale = options.maxScale;
if (options.speed) speed = options.speed;
if (options.height) {
height = options.height;
currentHeight = options.height;
}
if (options.color) {
color = options.color;
if (entity) {
entity.wall.material = color;
}
}
},
entity: entity
};
console.log('✅ 多边形扩散效果已创建');
} else {
// 移除多边形扩散效果
isShowPolygonDiffusion.value = false;
if (polygonDiffusionRef.value) {
polygonDiffusionRef.value.remove();
polygonDiffusionRef.value = null;
}
console.log('✅ 多边形扩散效果已移除');
}
// 强制场景更新
if (cesiumViewer.value && cesiumViewer.value.scene) {
cesiumViewer.value.scene.requestRender();
}
}四、总结
两种最常用的动态扩散墙特效
- 圆形扩散墙:适合点位预警、半径扫描、设备监控。
- 多边形扩散墙:适合电子围栏、区域警戒、不规则范围提示。
如果是生产环境我建议可以增加消除机制,避免出现无限制循环的情况发生。
