Appearance
Cesium 大屏告警特效实战:CSS遮罩+后期处理双实现,拷走就用!
接前文,之前说缺少一个告警特效。本来以为很简单的一个 class + 动画 还不就搞定了,结果给我调麻了。
这里采用两种方式实现一下告警提示的效果,也就是屏幕四周红色光晕+呼吸闪烁(游戏残血同款)。
纯CSS四边告警光晕(推荐)
在外层创建一个遮罩的 div 用于显示红色光晕的效果,增加一个动画闪烁,就完成了。
html
<div class="alarm-halo" v-if="isShowAlarm"></div>
<style>
.alarm-halo {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 9999;
background: radial-gradient(ellipse at center, transparent 60%, rgba(214, 18, 18, 0.2) 100%);
box-shadow: inset 0 0 80px 40px rgba(214, 18, 18, 0.4);
animation: alarmBlink 1s infinite alternate;
}
@keyframes alarmBlink {
0% {
opacity: 0.3;
}
100% {
opacity: 0.8;
}
}
</style>其实代码很简单,这里我踩了俩坑。
坑1
不要把 .alarm-halo 直接绑定在外层 div 上,这里我调了很久才发现问题所在。CSS遮罩被background-image覆盖,z-index失效、inset`阴影被完全遮挡。
这会导致屏幕无法正常显示红色的边缘,只显示白色的闪烁。
不光是background-image会遮挡,Cesium Canvas渲染层也会遮挡。
所以这里一定要创建一个单独的 div,控制一下层级显示。
坑2
style 代码记得写在最外层,或者是内页的 style 标签不要添加 scoped。
很基础的问题,但是硬是拌住我半小时,服了!
Cesium PostProcessStage 方案
基于Cesium内置的WebGL后期处理通道,直接在3D场景渲染完成后叠加屏幕边缘红色光晕,完全脱离DOM结构。
不存在任何层级遮挡问题,着色器控制边缘渐变,效果丝滑、高端、原生融合。
GLSL片段着色器实现屏幕四周渐变红边,动态uniform参数控制闪烁强度。
完整代码
javascript
// 初始化Cesium
const viewer = new Cesium.Viewer("cesiumContainer", {
timeline: false,
animation: false,
baseLayerPicker: false,
});
// 全局存储实例,防止重复创建
let alarmPostProcess = null;
let alarmAnimationTimer = null;
/**
* Cesium 后期处理告警光晕(永不被遮挡)
* @param {boolean} isShowAlarm true-显示告警 false-关闭告警
*/
function setCesiumAlarmPostProcess(isShowAlarm) {
if (!isShowAlarm) {
// 移除后期处理
if (alarmPostProcess) {
viewer.scene.postProcessStages.remove(alarmPostProcess);
alarmPostProcess = null;
}
// 清除动画定时器
if (alarmAnimationTimer) {
clearInterval(alarmAnimationTimer);
alarmAnimationTimer = null;
}
return;
}
if (alarmPostProcess) return;
// GLSL 告警着色器(核心)
const alarmShader = `
uniform float intensity;
void main() {
// 获取场景原始颜色
vec4 sourceColor = texture2D(colorTexture, v_textureCoordinates);
// 计算屏幕边缘渐变:中心0 → 边缘1
vec2 uv = v_textureCoordinates;
float vignette = 1.0 - length(uv - 0.5);
// 调整光晕范围:数值越大,红边越窄、越贴边
vignette = pow(vignette, 3.0);
// 边缘叠加红色,绿色通道、蓝色通道弱化,突出告警红
float red = sourceColor.r + vignette * intensity * 0.9;
float green = sourceColor.g * (1.0 - vignette * intensity * 0.2);
float blue = sourceColor.b * (1.0 - vignette * intensity * 0.2);
// 输出最终颜色
gl_FragColor = vec4(red, green, blue, sourceColor.a);
}
`;
alarmPostProcess = viewer.scene.postProcessStages.add(
new Cesium.PostProcessStage({
fragmentShader: alarmShader,
uniforms: {
intensity: 0.0, // 告警强度:0~1
},
})
);
let intensityValue = 0.0;
let isIncrease = true; // 递增/递减开关
// 每30ms更新一次强度,实现平滑呼吸效果
alarmAnimationTimer = setInterval(() => {
// 数值变化
intensityValue += isIncrease ? 0.05 : -0.05;
// 边界控制
if (intensityValue >= 0.9) isIncrease = false;
if (intensityValue <= 0.1) isIncrease = true;
// 赋值给着色器
if (alarmPostProcess) {
alarmPostProcess.uniforms.intensity = intensityValue;
}
}, 30);
}
// 开启Cesium原生告警效果
setCesiumAlarmPostProcess(true);
// 关闭告警效果
setCesiumAlarmPostProcess(false);总结
这里推荐最简单实现的 纯CSS 实现方案,并不推荐Cesium后期处理方案。
Cesium方案实现的效果其实一般,而且实现起来还比较复杂。
