feat(chart): add presentation mode
Some checks failed
Package component / package (push) Failing after 50s

This commit is contained in:
李启源
2026-07-29 12:12:03 +08:00
parent 312e7fde52
commit f97ff24cdf
22 changed files with 478 additions and 26 deletions

View File

@@ -7,9 +7,12 @@ import { ANNOTATION_TEXT_FONT, ANNOTATION_TEXT_LINE_HEIGHT } from './markup'
interface Props {
annotations: RenderedAnnotation[]
visible: boolean
interactive?: boolean
}
const props = defineProps<Props>()
const props = withDefaults(defineProps<Props>(), {
interactive: true,
})
const emit = defineEmits<{
(event: 'contextmenu', annotationId: string, mouseEvent: MouseEvent): void
(event: 'drag-start'): void
@@ -79,7 +82,7 @@ function scheduleDragFrame() {
}
function handlePointerDown(rendered: RenderedAnnotation, event: PointerEvent) {
if (event.button !== 0 || dragState) return
if (!props.interactive || event.button !== 0 || dragState) return
event.preventDefault()
event.stopPropagation()
const target = event.currentTarget as SVGGElement | null
@@ -108,6 +111,7 @@ function handlePointerDown(rendered: RenderedAnnotation, event: PointerEvent) {
}
function handlePointerMove(event: PointerEvent) {
if (!props.interactive) return
event.stopPropagation()
if (!dragState || event.pointerId !== dragState.pointerId) return
const deltaX = event.clientX - dragState.startX
@@ -119,6 +123,7 @@ function handlePointerMove(event: PointerEvent) {
}
function finishPointerDrag(event: PointerEvent) {
if (!props.interactive) return
event.preventDefault()
event.stopPropagation()
if (!dragState || event.pointerId !== dragState.pointerId) return
@@ -181,6 +186,11 @@ function handlePointerCancel(event: PointerEvent) {
event.preventDefault()
event.stopPropagation()
if (!dragState || event.pointerId !== dragState.pointerId) return
cancelActiveDrag()
}
function cancelActiveDrag() {
if (!dragState) return
const state = dragState
dragState = null
dragOffsets.value = new Map(dragOffsets.value).set(state.annotationId, { x: 0, y: 0 })
@@ -188,10 +198,21 @@ function handlePointerCancel(event: PointerEvent) {
cancelAnimationFrame(dragFrame)
dragFrame = null
}
if (state.target.hasPointerCapture(state.pointerId)) {
state.target.releasePointerCapture(state.pointerId)
}
emit('drag-end', true)
}
watch(
() => props.interactive,
(interactive) => {
if (!interactive) cancelActiveDrag()
},
)
function handleContextMenu(annotationId: string, event: MouseEvent) {
if (!props.interactive) return
event.preventDefault()
event.stopPropagation()
// 使用时间戳比较:如果 contextmenu 在拖动结束后 100ms 内触发,则抑制
@@ -206,7 +227,11 @@ function markerId(annotationId: string) {
</script>
<template>
<g v-if="props.visible" class="waveform-annotation-layer">
<g
v-if="props.visible"
class="waveform-annotation-layer"
:class="{ 'waveform-annotation-layer--interactive': props.interactive }"
>
<g
v-for="rendered in props.annotations"
:key="rendered.annotation.id"
@@ -281,13 +306,13 @@ function markerId(annotationId: string) {
pointer-events: none;
}
.waveform-annotation {
.waveform-annotation-layer--interactive .waveform-annotation {
pointer-events: auto;
cursor: grab;
touch-action: none;
}
.waveform-annotation:active {
.waveform-annotation-layer--interactive .waveform-annotation:active {
cursor: grabbing;
}
@@ -300,6 +325,9 @@ function markerId(annotationId: string) {
.waveform-annotation__box {
stroke-width: 1;
rx: 3;
}
.waveform-annotation-layer--interactive .waveform-annotation__box {
pointer-events: auto;
}

View File

@@ -38,6 +38,7 @@ interface AnnotationContext {
titleAreaHeight: ComputedRef<number>
chartTopMargin: ComputedRef<number>
activeInteractionMode: ComputedRef<WaveformInteractionMode | undefined>
isPresentationMode: ComputedRef<boolean>
}
interface AnnotationCandidateContext {
@@ -75,6 +76,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
titleAreaHeight,
chartTopMargin,
activeInteractionMode,
isPresentationMode,
} = context
function toggleSeriesVisibility(seriesId: string) {
@@ -140,6 +142,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function changeDraftSeries(seriesId: string) {
if (isPresentationMode.value) return
const draft = annotationInteraction.editorDraft.value
const candidate = editorSeriesOptions.value.find((item) => item.seriesId === seriesId)
const track = trackLayouts.value.find((item) =>
@@ -216,7 +219,13 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function handleAnnotationClick(event: MouseEvent, trackIndex?: number) {
if (!props.annotationsVisible || activeInteractionMode.value !== 'annotation') return
if (
isPresentationMode.value ||
!props.annotationsVisible ||
activeInteractionMode.value !== 'annotation'
) {
return
}
const candidateContext = resolveAnnotationCandidates(event, trackIndex)
if (!candidateContext) return
const nearby = nearbyCandidates(candidateContext.candidates)
@@ -238,7 +247,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function handleAnnotationContextMenu(event: MouseEvent, trackIndex?: number) {
if (!props.annotationsVisible) return
if (isPresentationMode.value || !props.annotationsVisible) return
event.preventDefault()
event.stopPropagation()
const candidateContext = resolveAnnotationCandidates(event, trackIndex)
@@ -252,7 +261,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function handleExistingAnnotationContextMenu(annotationId: string, event: MouseEvent) {
if (!props.annotationsVisible || !container.value) return
if (isPresentationMode.value || !props.annotationsVisible || !container.value) return
editorSeriesOptions.value = []
const annotation = props.annotations.find((item) => item.id === annotationId)
if (!annotation) return
@@ -270,6 +279,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function handleAnnotationMove(annotationId: string, offsetX: number, offsetY: number) {
if (isPresentationMode.value) return
const annotation = props.annotations.find((item) => item.id === annotationId)
if (!annotation || !Number.isFinite(offsetX) || !Number.isFinite(offsetY)) return
emit(
@@ -281,6 +291,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function editContextAnnotation() {
if (isPresentationMode.value) return
const menu = annotationInteraction.contextMenu.value
const annotation = props.annotations.find((item) => item.id === menu?.annotationId)
if (!annotation) return
@@ -302,6 +313,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function deleteContextAnnotation() {
if (isPresentationMode.value) return
const annotationId = annotationInteraction.contextMenu.value?.annotationId
const annotation = props.annotations.find((item) => item.id === annotationId)
if (!annotation) return
@@ -314,6 +326,7 @@ export function useWaveformChartAnnotations(context: AnnotationContext) {
}
function confirmAnnotation(annotation: WaveformAnnotation) {
if (isPresentationMode.value) return
const draft = annotationInteraction.editorDraft.value
if (!draft) return
if (draft.mode === 'add') {