优化时间线组件

- 更新时间轴操作说明
- 解决播放头在移动时的粗细变化
- 解决快速拖动播放头,松开鼠标后的播放头闪现
This commit is contained in:
yueliuli 2026-04-24 11:46:23 +08:00
parent e26be2cb89
commit 8cd8045649
3 changed files with 33 additions and 13 deletions

View File

@ -98,9 +98,19 @@
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 2px; width: 2px;
background-color: #ef4444;
z-index: 25; z-index: 25;
cursor: ew-resize; cursor: ew-resize;
transform: translateZ(0);
}
.playhead::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 100%;
background-color: #ef4444;
} }
.playhead-indicator { .playhead-indicator {

View File

@ -64,6 +64,7 @@ export function useTimeline(
const isPlayheadDragging = ref(false) const isPlayheadDragging = ref(false)
const isRulerDragging = ref(false) const isRulerDragging = ref(false)
const isMounted = ref(false) const isMounted = ref(false)
const displayFrame = ref(0)
const dragStartX = ref(0) const dragStartX = ref(0)
const scrollStartLeft = ref(0) const scrollStartLeft = ref(0)
const localCurrentFrame = ref(0) const localCurrentFrame = ref(0)
@ -121,18 +122,16 @@ export function useTimeline(
// 计算缩放前后的位置偏移 // 计算缩放前后的位置偏移
// console.log('container.scrollLeft', container.scrollLeft) // console.log('container.scrollLeft', container.scrollLeft)
if (playheadVisible) { if (playheadVisible) {
// 以播放头为中心缩放
const playheadX = playheadPosition - containerScrollLeft const playheadX = playheadPosition - containerScrollLeft
const newPlayheadPosition = props.currentFrame * newPxPerFrame const newPlayheadPosition = props.currentFrame * newPxPerFrame
const newScrollLeft = newPlayheadPosition - playheadX const newScrollLeft = Math.round(newPlayheadPosition - playheadX)
container.scrollLeft = newScrollLeft container.scrollLeft = newScrollLeft
} }
else { else {
// 以容器中心为中心缩放
const centerX = containerWidth / 2 const centerX = containerWidth / 2
const centerPosition = containerScrollLeft + centerX const centerPosition = containerScrollLeft + centerX
const newCenterPosition = centerPosition * (newPxPerFrame / oldPxPerFrame) const newCenterPosition = centerPosition * (newPxPerFrame / oldPxPerFrame)
container.scrollLeft = newCenterPosition - centerX container.scrollLeft = Math.round(newCenterPosition - centerX)
} }
// 最后更新缩放值 // 最后更新缩放值
@ -175,9 +174,7 @@ export function useTimeline(
} }
const playheadPos: ComputedRef<number> = computed(() => { const playheadPos: ComputedRef<number> = computed(() => {
const frame = isPlayheadDragging.value ? localCurrentFrame.value : props.currentFrame return Math.round(displayFrame.value * pxPerFrame.value)
const clampedFrame = Math.max(0, Math.min(frame, props.totalFrames - 1))
return clampedFrame * pxPerFrame.value
}) })
const trackRows: ComputedRef<HazardRow[][]> = computed(() => { const trackRows: ComputedRef<HazardRow[][]> = computed(() => {
@ -260,6 +257,15 @@ export function useTimeline(
if (!timelineContainer.value) if (!timelineContainer.value)
return return
e.preventDefault() e.preventDefault()
if (e.shiftKey) {
const delta = e.deltaY || e.deltaX
const zoomDelta = delta > 0 ? -0.1 : 0.1
const newZoom = pxPerFrame.value + zoomDelta
setZoom(newZoom)
return
}
const container = timelineContainer.value const container = timelineContainer.value
const delta = e.deltaY || e.deltaX const delta = e.deltaY || e.deltaX
container.scrollLeft += delta container.scrollLeft += delta
@ -318,6 +324,7 @@ export function useTimeline(
const frame = Math.floor(x / pxPerFrame.value) const frame = Math.floor(x / pxPerFrame.value)
const clampedFrame = Math.max(0, Math.min(frame, props.totalFrames - 1)) const clampedFrame = Math.max(0, Math.min(frame, props.totalFrames - 1))
localCurrentFrame.value = clampedFrame localCurrentFrame.value = clampedFrame
displayFrame.value = clampedFrame
emit('frameChange', clampedFrame) emit('frameChange', clampedFrame)
} }
@ -330,13 +337,16 @@ export function useTimeline(
const frame = Math.floor(x / pxPerFrame.value) const frame = Math.floor(x / pxPerFrame.value)
const clampedFrame = Math.max(0, Math.min(frame, props.totalFrames - 1)) const clampedFrame = Math.max(0, Math.min(frame, props.totalFrames - 1))
localCurrentFrame.value = clampedFrame localCurrentFrame.value = clampedFrame
displayFrame.value = clampedFrame
emit('frameChange', clampedFrame) emit('frameChange', clampedFrame)
} }
watch(() => props.currentFrame, () => { watch(() => props.currentFrame, () => {
if (!isPlayheadDragging.value) { if (isPlayheadDragging.value)
localCurrentFrame.value = props.currentFrame return
}
displayFrame.value = props.currentFrame
nextTick(() => { nextTick(() => {
if (!timelineContainer.value) if (!timelineContainer.value)
return return
@ -408,7 +418,7 @@ export function useTimeline(
isDragging, isDragging,
isPlayheadDragging, isPlayheadDragging,
isRulerDragging, isRulerDragging,
localCurrentFrame, displayFrame,
trackWidth, trackWidth,
timeTicks, timeTicks,
secondTicks, secondTicks,

View File

@ -50,7 +50,7 @@ onUnmounted(() => {
<el-button <el-button
type="info" dashed :icon="InfoFilled" size="small" type="info" dashed :icon="InfoFilled" size="small"
@click="openMessageBox( @click="openMessageBox(
'拖动播放头控制播放进度。滚轮/拖动时间轴前后移动。点击隐患编号跳转对应隐患。', '拖动播放头 控制播放进度。滚轮/拖动 时间轴前后移动。点击隐患编号跳转对应隐患。shift键+鼠标滚轮 缩放时间轴。',
'操作说明', '操作说明',
)" )"
> >