feat: 在播放控制器中添加本地时间管理和定时器逻辑,优化时间同步与用户交互体验
This commit is contained in:
parent
6a5a72828f
commit
6fb243370d
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CaretRightOutlined, PauseOutlined } from '@ant-design/icons-vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { computed, ref, watch, onUnmounted } from 'vue';
|
||||
|
||||
import { useTimelineTicks } from '../hooks/useTimelineTicks';
|
||||
|
||||
@ -21,6 +21,9 @@ const SLIDER_PADDING = 8; // Ant Design Slider has 8px padding on each side
|
||||
const HOUR_IN_MS = 3600 * 1000;
|
||||
|
||||
// --- Internal State ---
|
||||
const localCurrentTime = ref(props.currentTime);
|
||||
let timer: number | undefined;
|
||||
|
||||
const selectedHour = ref(Math.floor(props.currentTime / HOUR_IN_MS));
|
||||
const isUserInteracting = ref(false); // 标志位,防止 props 更新与用户操作冲突
|
||||
|
||||
@ -32,14 +35,48 @@ const viewEndTime = computed(() => viewStartTime.value + HOUR_IN_MS);
|
||||
const { ticks } = useTimelineTicks(viewStartTime, viewEndTime);
|
||||
|
||||
// --- Sync internal time with external prop ---
|
||||
watch(
|
||||
() => props.isPlaying,
|
||||
(playing) => {
|
||||
if (playing) {
|
||||
clearInterval(timer); // Clear any existing timer
|
||||
timer = window.setInterval(() => {
|
||||
localCurrentTime.value += 1000;
|
||||
}, 1000);
|
||||
} else {
|
||||
clearInterval(timer);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.currentTime,
|
||||
(newValue) => {
|
||||
// Only sync from prop if the difference is significant (e.g., > 1.5s)
|
||||
// This prevents conflicts between the internal timer and external updates during playback.
|
||||
const diff = Math.abs(newValue - localCurrentTime.value);
|
||||
if (Number.isFinite(newValue) && newValue >= 0 && diff > 1500) {
|
||||
localCurrentTime.value = newValue;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// --- Watch local time to sync UI components like hour selector ---
|
||||
watch(
|
||||
localCurrentTime,
|
||||
(newValue) => {
|
||||
// 如果用户正在主动交互(如拖动、点击),则暂时不根据外部时间更新小时选择
|
||||
if (isUserInteracting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 仅在有效范围内才同步小时;避免负数/异常 currentTime 破坏选择器
|
||||
if (Number.isFinite(newValue) && newValue >= 0 && props.totalDuration > 0) {
|
||||
const maxHour = Math.max(Math.ceil(props.totalDuration / HOUR_IN_MS) - 1, 0);
|
||||
@ -62,13 +99,13 @@ 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(props.currentTime));
|
||||
const currentTimeFormatted = computed(() => formatTime(localCurrentTime.value));
|
||||
const totalDurationFormatted = computed(() => formatTime(props.totalDuration));
|
||||
|
||||
const playheadPosition = computed(() => {
|
||||
const viewDuration = viewEndTime.value - viewStartTime.value;
|
||||
if (viewDuration <= 0) return '0%';
|
||||
const current = props.currentTime;
|
||||
const current = localCurrentTime.value;
|
||||
const relativeTime = current - viewStartTime.value;
|
||||
const clamped = Math.min(Math.max(relativeTime, 0), viewDuration);
|
||||
return `${(clamped / viewDuration) * 100}%`;
|
||||
@ -105,6 +142,7 @@ const handleHourChange = (newHour: number) => {
|
||||
const newTime = newHour * HOUR_IN_MS;
|
||||
// 确保搜寻时间不超过总时长
|
||||
const clampedTime = Math.min(newTime, props.totalDuration);
|
||||
localCurrentTime.value = clampedTime; // Immediately update local time
|
||||
emit('seek', clampedTime);
|
||||
};
|
||||
|
||||
@ -128,6 +166,7 @@ const handleTimelineClick = (event: MouseEvent) => {
|
||||
const ratio = clampedX / effectiveWidth;
|
||||
const viewDuration = viewEndTime.value - viewStartTime.value;
|
||||
const targetTime = Math.floor(viewStartTime.value + ratio * viewDuration);
|
||||
localCurrentTime.value = targetTime; // Immediately update local time
|
||||
emit('seek', targetTime);
|
||||
// 点击发生后,立即更新所选小时,保持左侧小时选择与视窗一致
|
||||
const clickedHour = Math.floor(targetTime / HOUR_IN_MS);
|
||||
|
Loading…
x
Reference in New Issue
Block a user