新增定位到SCADA设备时的动画闪烁标识
This commit is contained in:
@@ -81,11 +81,13 @@ const fetchFromBackend = async (
|
||||
const ids = deviceIds.join(",");
|
||||
const starttime = dayjs(range.from).format("YYYY-MM-DD HH:mm:ss");
|
||||
const endtime = dayjs(range.to).format("YYYY-MM-DD HH:mm:ss");
|
||||
// 清洗数据接口
|
||||
const cleaningSCADAUrl = `${config.backendUrl}/querycleaningscadadatabydeviceidandtimerange/?ids=${ids}&starttime=${starttime}&endtime=${endtime}`;
|
||||
|
||||
const url = `${config.backendUrl}/querycleaningscadadatabydeviceidandtimerange/?ids=${ids}&starttime=${starttime}&endtime=${endtime}`;
|
||||
const originSCADAUrl = `${config.backendUrl}/queryscadadatabydeviceidandtimerange/?ids=${ids}&starttime=${starttime}&endtime=${endtime}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const response = await fetch(cleaningSCADAUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
@@ -317,11 +319,7 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
|
||||
});
|
||||
setTimeSeries(result);
|
||||
setLoadingState("success");
|
||||
console.debug(
|
||||
`[SCADADataPanel] 数据刷新成功 (${reason}),共 ${result.length} 条记录。`
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("[SCADADataPanel] 获取时序数据失败", err);
|
||||
setError(err instanceof Error ? err.message : "未知错误");
|
||||
setLoadingState("error");
|
||||
}
|
||||
|
||||
@@ -44,11 +44,12 @@ import { useMap } from "@app/OlMap/MapComponent";
|
||||
import { GeoJSON } from "ol/format";
|
||||
import VectorLayer from "ol/layer/Vector";
|
||||
import VectorSource from "ol/source/Vector";
|
||||
import { Stroke, Style, Circle } from "ol/style";
|
||||
import { Stroke, Style, Circle, Fill } from "ol/style";
|
||||
import Feature from "ol/Feature";
|
||||
import { Point } from "ol/geom";
|
||||
import { getVectorContext } from "ol/render";
|
||||
import { unByKey } from "ol/Observable";
|
||||
import config from "@/config/config";
|
||||
import { get } from "http";
|
||||
|
||||
const STATUS_OPTIONS: {
|
||||
value: "online" | "offline" | "warning" | "error";
|
||||
@@ -105,6 +106,8 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
||||
const [highlightLayer, setHighlightLayer] =
|
||||
useState<VectorLayer<VectorSource> | null>(null);
|
||||
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
||||
const [blinkingFeature, setBlinkingFeature] = useState<Feature | null>(null);
|
||||
const blinkListenerKeyRef = useRef<any>(null);
|
||||
|
||||
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
@@ -294,9 +297,117 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
||||
|
||||
// 处理缩放到设备
|
||||
const handleZoomToDevice = (device: SCADADevice) => {
|
||||
map
|
||||
?.getView()
|
||||
.fit(new Point(device.coordinates), { maxZoom: 15, duration: 1000 });
|
||||
if (!map) return;
|
||||
|
||||
// 缩放到设备位置
|
||||
map.getView().fit(new Point(device.coordinates), {
|
||||
maxZoom: 18,
|
||||
duration: 1000,
|
||||
});
|
||||
|
||||
// 创建闪烁效果
|
||||
createBlinkingEffect(device.coordinates);
|
||||
};
|
||||
|
||||
// 创建闪烁效果
|
||||
const createBlinkingEffect = (coordinates: [number, number]) => {
|
||||
if (!map || !highlightLayer) return;
|
||||
|
||||
// 清除之前的闪烁效果
|
||||
if (blinkListenerKeyRef.current) {
|
||||
unByKey(blinkListenerKeyRef.current);
|
||||
blinkListenerKeyRef.current = null;
|
||||
}
|
||||
|
||||
// 创建闪烁点要素
|
||||
const blinkFeature = new Feature({
|
||||
geometry: new Point(coordinates),
|
||||
});
|
||||
setBlinkingFeature(blinkFeature);
|
||||
|
||||
const duration = 2000; // 闪烁持续时间
|
||||
const start = Date.now();
|
||||
|
||||
// 使用图层的 postrender 事件实现闪烁动画
|
||||
const listenerKey = highlightLayer.on("postrender", (event) => {
|
||||
const elapsed = Date.now() - start;
|
||||
if (elapsed > duration) {
|
||||
// 动画结束
|
||||
unByKey(listenerKey);
|
||||
setBlinkingFeature(null);
|
||||
blinkListenerKeyRef.current = null;
|
||||
map.render(); // 最后渲染一次以清除效果
|
||||
return;
|
||||
}
|
||||
|
||||
const vectorContext = getVectorContext(event);
|
||||
const flashGeom = blinkFeature.getGeometry();
|
||||
|
||||
if (!flashGeom) return;
|
||||
|
||||
// 计算闪烁效果
|
||||
const progress = elapsed / duration;
|
||||
// 使用正弦波创建脉冲效果,增加频率以产生更快的闪烁
|
||||
const rawOpacity =
|
||||
Math.abs(Math.sin(progress * Math.PI * 6)) * (1 - progress);
|
||||
// 确保 opacity 不会太小,避免科学计数法导致的颜色解析错误
|
||||
const opacity = Math.max(rawOpacity, 0.01);
|
||||
const radius = 10 + (1 - progress) * 10; // 从20逐渐减小到10
|
||||
|
||||
// 当透明度太低时直接跳过渲染
|
||||
if (opacity < 0.02) {
|
||||
map.render();
|
||||
return;
|
||||
}
|
||||
|
||||
// 绘制外圈(黄色光晕)
|
||||
vectorContext.setStyle(
|
||||
new Style({
|
||||
image: new Circle({
|
||||
radius: radius * 1.5,
|
||||
fill: new Fill({
|
||||
color: `rgba(255, 255, 0, ${(opacity * 0.3).toFixed(3)})`,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
vectorContext.drawGeometry(flashGeom);
|
||||
|
||||
// 绘制中圈(亮黄色)
|
||||
vectorContext.setStyle(
|
||||
new Style({
|
||||
image: new Circle({
|
||||
radius: radius,
|
||||
fill: new Fill({
|
||||
color: `rgba(255, 255, 0, ${(opacity * 0.6).toFixed(3)})`,
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: `rgba(255, 200, 0, ${opacity.toFixed(3)})`,
|
||||
width: 2,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
vectorContext.drawGeometry(flashGeom);
|
||||
|
||||
// 绘制内核(白色中心)
|
||||
vectorContext.setStyle(
|
||||
new Style({
|
||||
image: new Circle({
|
||||
radius: radius * 0.3,
|
||||
fill: new Fill({
|
||||
color: `rgba(255, 255, 255, ${opacity.toFixed(3)})`,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
vectorContext.drawGeometry(flashGeom);
|
||||
|
||||
// 继续渲染下一帧
|
||||
map.render();
|
||||
});
|
||||
|
||||
blinkListenerKeyRef.current = listenerKey;
|
||||
};
|
||||
|
||||
// 清除搜索
|
||||
@@ -352,6 +463,11 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
||||
setHighlightLayer(highlightLayer);
|
||||
|
||||
return () => {
|
||||
// 清除闪烁效果监听器
|
||||
if (blinkListenerKeyRef.current) {
|
||||
unByKey(blinkListenerKeyRef.current);
|
||||
blinkListenerKeyRef.current = null;
|
||||
}
|
||||
map.removeLayer(highlightLayer);
|
||||
};
|
||||
}, [map]);
|
||||
@@ -639,9 +755,9 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
||||
sx={{ fontSize: "0.7rem", height: 20 }}
|
||||
/>
|
||||
<Chip
|
||||
label={
|
||||
"可靠度" + getReliability(device.reliability)
|
||||
}
|
||||
label={`可靠度: ${getReliability(
|
||||
device.reliability
|
||||
)}`}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
sx={{ fontSize: "0.7rem", height: 20 }}
|
||||
|
||||
Reference in New Issue
Block a user