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