feat: 优化播放控制器和 WebSocket 逻辑,移除不必要的内部状态,增强场景加载后机器人初始化流程
This commit is contained in:
parent
e3f3ab5c2a
commit
3420c7cc9c
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CaretRightOutlined, PauseOutlined } from '@ant-design/icons-vue';
|
||||
import { computed, ref, watch, onUnmounted } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { useTimelineTicks } from '../hooks/useTimelineTicks';
|
||||
|
||||
@ -21,7 +21,6 @@ const SLIDER_PADDING = 8; // Ant Design Slider has 8px padding on each side
|
||||
const HOUR_IN_MS = 3600 * 1000;
|
||||
|
||||
// --- Internal State ---
|
||||
const internalCurrentTime = ref(props.currentTime);
|
||||
const selectedHour = ref(Math.floor(props.currentTime / HOUR_IN_MS));
|
||||
|
||||
// --- Computed View Range ---
|
||||
@ -36,9 +35,6 @@ watch(
|
||||
() => props.currentTime,
|
||||
(newValue) => {
|
||||
// Only update from prop if not playing
|
||||
if (!props.isPlaying) {
|
||||
internalCurrentTime.value = newValue;
|
||||
}
|
||||
const newHour = Math.floor(newValue / HOUR_IN_MS);
|
||||
if (newHour !== selectedHour.value) {
|
||||
selectedHour.value = newHour;
|
||||
@ -47,54 +43,6 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// --- Animation Frame Timer for Playback ---
|
||||
let animationFrameId: number | null = null;
|
||||
let lastTimestamp: number | null = null;
|
||||
|
||||
const animationLoop = (timestamp: number) => {
|
||||
if (!lastTimestamp) {
|
||||
lastTimestamp = timestamp;
|
||||
}
|
||||
const deltaTime = timestamp - lastTimestamp;
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
// Ensure we don't jump too far ahead if the tab was inactive
|
||||
const clampedDelta = Math.min(deltaTime, 1000); // Max 1-second jump
|
||||
|
||||
const newTime = internalCurrentTime.value + clampedDelta;
|
||||
|
||||
if (newTime < props.totalDuration) {
|
||||
internalCurrentTime.value = newTime;
|
||||
// No need to emit timeupdate as per user decision
|
||||
// emit('timeupdate', newTime);
|
||||
animationFrameId = requestAnimationFrame(animationLoop);
|
||||
} else {
|
||||
internalCurrentTime.value = props.totalDuration;
|
||||
emit('pause'); // Auto-pause at the end
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.isPlaying,
|
||||
(isPlaying) => {
|
||||
if (isPlaying) {
|
||||
lastTimestamp = null; // Reset timestamp on play
|
||||
animationFrameId = requestAnimationFrame(animationLoop);
|
||||
} else {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
animationFrameId = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
});
|
||||
|
||||
// --- Computed properties for display and binding ---
|
||||
const formatTime = (ms: number): string => {
|
||||
if (isNaN(ms) || ms < 0) return '00:00:00';
|
||||
@ -105,20 +53,19 @@ const formatTime = (ms: number): string => {
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const currentTimeFormatted = computed(() => formatTime(internalCurrentTime.value));
|
||||
const currentTimeFormatted = computed(() => formatTime(props.currentTime));
|
||||
const totalDurationFormatted = computed(() => formatTime(props.totalDuration));
|
||||
|
||||
const sliderValue = computed({
|
||||
get: () => internalCurrentTime.value,
|
||||
get: () => props.currentTime,
|
||||
set: (val: number) => {
|
||||
internalCurrentTime.value = val;
|
||||
emit('seek', val);
|
||||
},
|
||||
});
|
||||
|
||||
const playheadPosition = computed(() => {
|
||||
if (!props.totalDuration) return '0%';
|
||||
const relativeTime = internalCurrentTime.value - viewStartTime.value;
|
||||
const relativeTime = props.currentTime - viewStartTime.value;
|
||||
const viewDuration = viewEndTime.value - viewStartTime.value;
|
||||
return `${(relativeTime / viewDuration) * 100}%`;
|
||||
});
|
||||
@ -149,7 +96,6 @@ const handleTimelineClick = (event: MouseEvent) => {
|
||||
const newTimeInView = percentage * HOUR_IN_MS;
|
||||
const newAbsoluteTime = viewStartTime.value + newTimeInView;
|
||||
|
||||
internalCurrentTime.value = newAbsoluteTime;
|
||||
emit('seek', newAbsoluteTime);
|
||||
};
|
||||
|
||||
|
@ -62,11 +62,10 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
|
||||
});
|
||||
client.value = wsInstance;
|
||||
|
||||
wsInstance.onopen = () => {
|
||||
isConnected.value = true;
|
||||
message.success('回放连接已建立');
|
||||
startRenderLoop();
|
||||
};
|
||||
// [关键修复] ws.create() 返回时连接已建立,直接执行连接成功逻辑
|
||||
isConnected.value = true;
|
||||
message.success('回放连接已建立');
|
||||
startRenderLoop();
|
||||
|
||||
wsInstance.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data) as PlaybackMessage;
|
||||
@ -79,6 +78,7 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
|
||||
sceneJson.value = msg.data;
|
||||
latestRobotData.clear();
|
||||
} else if (msg.type === 'AMR') {
|
||||
console.log('[Playback] Received AMR data:', msg.data);
|
||||
(msg.data as RobotRealtimeInfo[]).forEach(robot => {
|
||||
latestRobotData.set(robot.id, robot);
|
||||
});
|
||||
@ -138,11 +138,15 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
|
||||
};
|
||||
|
||||
const batchUpdateRobots = (updates: Array<{ id: string; data: RobotRealtimeInfo }>) => {
|
||||
|
||||
const editor = editorService.value;
|
||||
console.log('[Playback] Batch update: Editor service is', editor ? 'available' : 'NOT available', '. Updates to process:', updates);
|
||||
if (!editor || updates.length === 0) return;
|
||||
|
||||
updates.forEach(({ id, data }) => {
|
||||
if (editor.checkRobotById(id)) {
|
||||
const robotExists = editor.checkRobotById(id);
|
||||
console.log(`[Playback] Checking robot ID: ${id}. Exists in editor: ${robotExists}`);
|
||||
if (robotExists) {
|
||||
const { x, y, angle, ...rest } = data;
|
||||
editor.updateRobot(id, rest);
|
||||
editor.setValue({
|
||||
@ -157,22 +161,30 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
|
||||
};
|
||||
|
||||
const renderLoop = () => {
|
||||
const updates: Array<{ id: string; data: RobotRealtimeInfo }> = [];
|
||||
for (const [id, data] of latestRobotData.entries()) {
|
||||
updates.push({ id, data });
|
||||
try {
|
||||
console.log('[Playback] Render loop executing...');
|
||||
const updates: Array<{ id: string; data: RobotRealtimeInfo }> = [];
|
||||
for (const [id, data] of latestRobotData.entries()) {
|
||||
updates.push({ id, data });
|
||||
}
|
||||
latestRobotData.clear();
|
||||
|
||||
if (updates.length > 0) {
|
||||
console.log(`[Playback] Render loop: Preparing to update ${updates.length} robots.`);
|
||||
batchUpdateRobots(updates);
|
||||
}
|
||||
|
||||
// if (isPlaying.value && currentTime.value >= totalDuration.value && totalDuration.value > 0) {
|
||||
// pause();
|
||||
// currentTime.value = totalDuration.value;
|
||||
// }
|
||||
|
||||
editorService.value?.render();
|
||||
} catch (error) {
|
||||
console.error('[Playback] Error in renderLoop:', error);
|
||||
stopRenderLoop(); // Stop the loop on error to prevent flooding the console
|
||||
return; // Exit the function
|
||||
}
|
||||
latestRobotData.clear();
|
||||
|
||||
if (updates.length > 0) {
|
||||
batchUpdateRobots(updates);
|
||||
}
|
||||
|
||||
// if (isPlaying.value && currentTime.value >= totalDuration.value && totalDuration.value > 0) {
|
||||
// pause();
|
||||
// currentTime.value = totalDuration.value;
|
||||
// }
|
||||
|
||||
editorService.value?.render();
|
||||
animationFrameId = requestAnimationFrame(renderLoop);
|
||||
};
|
||||
|
||||
|
@ -97,9 +97,11 @@ const handleDateChange = (date: Dayjs) => {
|
||||
}
|
||||
};
|
||||
|
||||
watch(playback.sceneJson, (newJson) => {
|
||||
watch(playback.sceneJson, async (newJson) => {
|
||||
if (newJson) {
|
||||
editor.value?.load(newJson);
|
||||
await editor.value?.load(newJson);
|
||||
// [关键修复] 场景加载后,立即重新初始化机器人
|
||||
await editor.value?.initRobots();
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user