diff --git a/src/components/hazard_inspect/timeline/timeline.ts b/src/components/hazard_inspect/timeline/timeline.ts index ea4ae1b..17b959a 100644 --- a/src/components/hazard_inspect/timeline/timeline.ts +++ b/src/components/hazard_inspect/timeline/timeline.ts @@ -39,8 +39,8 @@ export function useTimeline( ) { const FPS = 30 const pxPerFrame = ref(2) - const minPxPerFrame = 0.5 - const maxPxPerFrame = 3 + const minPxPerFrame = ref(0.5) + const maxPxPerFrame = ref(3) const timelineContainer: Ref = ref(null) const isDragging = ref(false) const isPlayheadDragging = ref(false) @@ -58,15 +58,54 @@ export function useTimeline( }) function updatePxPerFrame(): void { - if (!timelineContainer.value) + if (!timelineContainer.value || props.totalFrames === 0) return const containerWidth = timelineContainer.value.clientWidth - 20 const calculatedPxPerFrame = containerWidth / props.totalFrames - pxPerFrame.value = Math.max(minPxPerFrame, Math.min(calculatedPxPerFrame, maxPxPerFrame)) + // 计算最小缩放倍数,确保能看到视频全长 + const newMinPxPerFrame = Math.max(0.1, containerWidth / props.totalFrames) + // 只在必要时更新,避免无限循环 + if (Math.abs(newMinPxPerFrame - minPxPerFrame.value) > 0.01) { + minPxPerFrame.value = newMinPxPerFrame + } + // 最大值固定为3,不修改 + pxPerFrame.value = Math.max(minPxPerFrame.value, Math.min(calculatedPxPerFrame, maxPxPerFrame.value)) } function setZoom(value: number): void { - pxPerFrame.value = Math.max(minPxPerFrame, Math.min(value, maxPxPerFrame)) + const oldPxPerFrame = pxPerFrame.value + const newPxPerFrame = Math.max(minPxPerFrame.value, Math.min(value, maxPxPerFrame.value)) + + if (oldPxPerFrame === newPxPerFrame || !timelineContainer.value) + return + + const container = timelineContainer.value + const containerWidth = container.clientWidth + const containerScrollLeft = container.scrollLeft + const playheadPosition = props.currentFrame * oldPxPerFrame + + // 检查播放头是否在可见范围内 + const playheadVisible = playheadPosition >= containerScrollLeft + && playheadPosition <= containerScrollLeft + containerWidth + + // 计算缩放前后的位置偏移 + if (playheadVisible) { + // 以播放头为中心缩放 + const playheadX = playheadPosition - containerScrollLeft + const newPlayheadPosition = props.currentFrame * newPxPerFrame + const newScrollLeft = newPlayheadPosition - playheadX + container.scrollLeft = newScrollLeft + } + else { + // 以容器中心为中心缩放 + const centerX = containerWidth / 2 + const centerPosition = containerScrollLeft + centerX + const newCenterPosition = centerPosition * (newPxPerFrame / oldPxPerFrame) + container.scrollLeft = newCenterPosition - centerX + } + + // 最后更新缩放值 + pxPerFrame.value = newPxPerFrame } const timeTicks: ComputedRef = computed(() => { @@ -105,10 +144,9 @@ export function useTimeline( } const playheadPos: ComputedRef = computed(() => { - if (isPlayheadDragging.value) { - return localCurrentFrame.value * pxPerFrame.value - } - return props.currentFrame * pxPerFrame.value + const frame = isPlayheadDragging.value ? localCurrentFrame.value : props.currentFrame + const clampedFrame = Math.max(0, Math.min(frame, props.totalFrames - 1)) + return clampedFrame * pxPerFrame.value }) const trackRows: ComputedRef = computed(() => { @@ -281,7 +319,10 @@ export function useTimeline( if (!progressContainer) return - updatePxPerFrame() + // 使用 nextTick 确保容器已渲染 + nextTick(() => { + updatePxPerFrame() + }) progressContainer.addEventListener('wheel', handleWheel, { passive: false }) progressContainer.addEventListener('mousedown', handleMouseDown) @@ -291,11 +332,32 @@ export function useTimeline( } watch(() => props.totalFrames, () => { - nextTick(() => { + // 使用 setTimeout 避免频繁更新 + setTimeout(() => { updatePxPerFrame() - }) + }, 100) }) + // 移除 resize 监听,避免无限循环 + // 监听容器宽度变化 + // let resizeObserver: ResizeObserver | null = null + + // function handleResize(): void { + // updatePxPerFrame() + // } + + // watch(() => timelineContainer.value, (newVal, oldVal) => { + // if (newVal && !oldVal) { + // // 使用 ResizeObserver 代替 window resize 监听 + // resizeObserver = new ResizeObserver(handleResize) + // resizeObserver.observe(newVal) + // } + // else if (!newVal && oldVal && resizeObserver) { + // resizeObserver.disconnect() + // resizeObserver = null + // } + // }) + function initUnmounted(): void { window.removeEventListener('mousemove', handleMouseMove) window.removeEventListener('mousemove', handlePlayheadDrag) diff --git a/src/components/hazard_inspect/timeline/timeline.vue b/src/components/hazard_inspect/timeline/timeline.vue index cdb457d..196b630 100644 --- a/src/components/hazard_inspect/timeline/timeline.vue +++ b/src/components/hazard_inspect/timeline/timeline.vue @@ -1,4 +1,5 @@