From aaeb0c4f4f667e81aa871ccf9be47c58183e8cd2 Mon Sep 17 00:00:00 2001 From: yueliuli <1628111725@qq.com> Date: Wed, 22 Apr 2026 11:51:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=BA=BF=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=20-=20=E6=97=B6=E9=97=B4=E7=BA=BF=E7=BC=A9?= =?UTF-8?q?=E6=94=BE=E6=BB=91=E5=9D=97=E7=9A=84=E6=95=B0=E5=80=BC=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E9=99=90=E5=AE=9A=E5=B0=8F=E6=95=B0=E7=82=B9=E5=90=8E?= =?UTF-8?q?=E4=B8=A4=E4=BD=8D=20-=20=E6=97=B6=E9=97=B4=E7=BA=BF=E7=BC=A9?= =?UTF-8?q?=E6=94=BE=E6=97=B6=EF=BC=8C=E5=A6=82=E6=9E=9C=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=A4=B4=E5=9C=A8=E6=97=B6=E9=97=B4=E7=BA=BF=E8=8C=83=E5=9B=B4?= =?UTF-8?q?=E5=86=85=EF=BC=8C=E4=BB=A5=E6=92=AD=E6=94=BE=E5=A4=B4=E4=B8=BA?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E7=BC=A9=E6=94=BE=EF=BC=8C=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E7=9C=8B=E4=B8=8D=E5=88=B0=E6=92=AD=E6=94=BE=E5=A4=B4=EF=BC=8C?= =?UTF-8?q?=E5=88=99=E4=BB=A5=E6=97=B6=E9=97=B4=E7=BA=BF=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E7=BC=A9=E6=94=BE=20-=20=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E7=BA=BF=E5=9C=A8=E5=8A=A0=E8=BD=BD=E6=97=B6=E8=87=AA=E9=80=82?= =?UTF-8?q?=E5=BA=94=E6=9C=80=E5=B0=8F=E7=BC=A9=E6=94=BE=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hazard_inspect/timeline/timeline.ts | 86 ++++++++++++++++--- .../hazard_inspect/timeline/timeline.vue | 5 +- 2 files changed, 77 insertions(+), 14 deletions(-) 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 @@