feat: 优化机器人状态监控,采用 requestAnimationFrame 实现高效渲染,提升动画流畅性并避免UI阻塞
This commit is contained in:
parent
3192cb814e
commit
2f3bfa9bc4
@ -47,6 +47,7 @@ const readScene = async () => {
|
||||
|
||||
/**
|
||||
* 监控场景中的机器人状态
|
||||
* 采用 requestAnimationFrame 优化高频数据渲染,确保动画流畅且不阻塞UI线程。
|
||||
*/
|
||||
const monitorScene = async () => {
|
||||
console.log(current.value?.id);
|
||||
@ -54,17 +55,74 @@ const monitorScene = async () => {
|
||||
// 根据路由路径是否是真实场景监控决定使用哪个监控接口
|
||||
const ws = isMonitorMode.value ? await monitorRealSceneById(props.sid) : await monitorSceneById(props.sid);
|
||||
if (isNil(ws)) return;
|
||||
ws.onmessage = (e) => {
|
||||
const { id, x, y, active, angle, path, isWaring, isFault, ...rest } = <RobotRealtimeInfo>JSON.parse(e.data || '{}');
|
||||
|
||||
// 使用 Map 来缓冲每个机器人最新的实时数据。
|
||||
// Key 是机器人 ID,Value 是该机器人最新的完整信息。
|
||||
// 这样做可以确保我们总是有每个机器人最新的状态,并且可以快速更新,避免重复渲染。
|
||||
const latestRobotData = new Map<string, RobotRealtimeInfo>();
|
||||
|
||||
// 用于存储 requestAnimationFrame 的 ID,方便在组件卸载或 WebSocket 关闭时取消动画循环。
|
||||
let animationFrameId: number;
|
||||
|
||||
/**
|
||||
* 渲染循环函数。
|
||||
* 这个函数会通过 requestAnimationFrame 以浏览器的刷新频率(通常是60fps)被反复调用。
|
||||
* 这是实现流畅动画的核心。
|
||||
*/
|
||||
const renderLoop = () => {
|
||||
// 检查是否有新的数据需要渲染。如果没有,则本次循环不执行任何操作,节省性能。
|
||||
if (latestRobotData.size > 0) {
|
||||
// 遍历所有待更新的机器人数据
|
||||
latestRobotData.forEach((data, id) => {
|
||||
const { x, y, active, angle, path, isWaring, isFault, ...rest } = data;
|
||||
// 确保机器人仍然存在于编辑器中
|
||||
if (!editor.value?.checkRobotById(id)) return;
|
||||
|
||||
// 更新机器人的非位置属性(如状态等)
|
||||
editor.value?.updateRobot(id, rest);
|
||||
|
||||
// 更新机器人的位置和可见性
|
||||
if (isNil(x) || isNil(y)) {
|
||||
// 如果坐标为空,则隐藏机器人
|
||||
editor.value.updatePen(id, { visible: false });
|
||||
} else {
|
||||
// 如果坐标有效,则刷新机器人在画布上的位置、角度等
|
||||
editor.value.refreshRobot(id, { x, y, active, angle, path, isWaring, isFault });
|
||||
}
|
||||
});
|
||||
// 本次渲染完成后,清空数据缓冲区,等待下一批新数据的到来。
|
||||
latestRobotData.clear();
|
||||
}
|
||||
// 请求浏览器在下一次重绘之前再次调用 renderLoop,形成动画循环。
|
||||
animationFrameId = requestAnimationFrame(renderLoop);
|
||||
};
|
||||
|
||||
/**
|
||||
* WebSocket 的 onmessage 事件处理器。
|
||||
* 这个函数只做一件事:接收数据并将其快速存入缓冲区。
|
||||
* 这种“数据与渲染分离”的设计是避免UI阻塞的关键。
|
||||
*/
|
||||
ws.onmessage = (e) => {
|
||||
const data = <RobotRealtimeInfo>JSON.parse(e.data || '{}');
|
||||
// 将最新数据存入 Map,如果已有相同ID的数据,则会被自动覆盖。
|
||||
latestRobotData.set(data.id, data);
|
||||
};
|
||||
|
||||
client.value = ws;
|
||||
|
||||
// WebSocket 连接成功后,立即启动渲染循环。
|
||||
renderLoop();
|
||||
|
||||
// 增强 WebSocket 的 onclose 事件,以确保在连接关闭时清理资源。
|
||||
const originalOnClose = ws.onclose;
|
||||
ws.onclose = (event) => {
|
||||
// 停止渲染循环,防止不必要的计算和内存泄漏。
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
// 如果原始的 onclose 处理函数存在,则继续执行它。
|
||||
if (originalOnClose) {
|
||||
originalOnClose.call(ws, event);
|
||||
}
|
||||
};
|
||||
};
|
||||
//#endregion
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user