waveform-analysis 0.1.76 → 0.1.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,12 @@ import { chartExportOptions } from '../controls/chartExportOptions'
13
13
  import { layoutAnnotations } from '../annotation/annotationLayout'
14
14
  import { useWaveformInstanceId } from '@/utils/waveformId'
15
15
 
16
- import { animationLayoutSignature, wrapAnimationCommands, type AnimationContext } from './context'
16
+ import {
17
+ animationGridShape,
18
+ animationLayoutSignature,
19
+ wrapAnimationCommands,
20
+ type AnimationContext,
21
+ } from './context'
17
22
  import { createPaginationPresentation } from './paginationPresentation'
18
23
 
19
24
  export function useWaveformAnimations(context: AnimationContext) {
@@ -26,6 +31,8 @@ export function useWaveformAnimations(context: AnimationContext) {
26
31
  const seen = new Set<string>()
27
32
  const prefix = useWaveformInstanceId('waveform-animation')
28
33
  let clipSequence = 0
34
+ let presentationGeneration = 0
35
+ let previousGrid = animationGridShape(context)
29
36
  let previousAnnotations = props.annotations
30
37
  let previousData = props.data
31
38
  let previousLayout = ''
@@ -63,9 +70,12 @@ export function useWaveformAnimations(context: AnimationContext) {
63
70
  states.has(track.id) ? projectTrack(states.get(track.id)!) : track,
64
71
  )
65
72
  })
66
- const pagination = createPaginationPresentation(context, controller, config, effective, () =>
67
- commit(true),
68
- )
73
+ const pagination = createPaginationPresentation(context, controller, config, effective, () => {
74
+ commit(true)
75
+ for (const track of tracks.value) for (const series of track.seriesList) seen.delete(series.id)
76
+ skipSignature = undefined
77
+ annotations.clear()
78
+ })
69
79
  const projectedAnnotations = computed(() => {
70
80
  const anchors = new Map([...states.values()].flatMap((state) => [...state.anchors]))
71
81
  const values = visibleAnnotations().flatMap((annotation) => {
@@ -100,12 +110,10 @@ export function useWaveformAnimations(context: AnimationContext) {
100
110
  [...state.series].map(([id, series]) => [id, series.opacity] as const),
101
111
  ),
102
112
  )
103
- return annotations.project(projectedAnnotations.value, opacities).map((annotation) => ({
104
- ...annotation,
105
- seriesOpacity: (annotation.seriesOpacity ?? 1) * pagination.opacity.value,
106
- }))
113
+ return annotations.project(projectedAnnotations.value, opacities)
107
114
  })
108
115
  function commit(preservePagination = false) {
116
+ presentationGeneration++
109
117
  if (!preservePagination) pagination.reset()
110
118
  const previous = new Map(states)
111
119
  states.clear()
@@ -130,6 +138,8 @@ export function useWaveformAnimations(context: AnimationContext) {
130
138
  if (disposed) return
131
139
  const nextConfig = config.value
132
140
  const dataChanged = props.data !== previousData
141
+ // The post-flush grid watcher waits for lifecycle page resets before requesting replay.
142
+ if (!dataChanged && seen.size && animationGridShape(context) !== previousGrid) return
133
143
  if (dataChanged) {
134
144
  pendingFrom = new Map(states)
135
145
  previousData = props.data
@@ -162,8 +172,9 @@ export function useWaveformAnimations(context: AnimationContext) {
162
172
  previousTracks = tracks.value
163
173
  previousSamplingState = sampling.state.value
164
174
  controller.setEnabled(effective.value)
165
- if (pagination.reconcile(dataChanged)) return
166
- if (!effective.value || layoutChanged) {
175
+ const replay = pagination.reconcile(dataChanged)
176
+ if (replay === 'static') return
177
+ if (!effective.value || (layoutChanged && !replay)) {
167
178
  controller.finishAll()
168
179
  commit()
169
180
  return
@@ -224,6 +235,7 @@ export function useWaveformAnimations(context: AnimationContext) {
224
235
  if (
225
236
  !seen.has(id) &&
226
237
  nextConfig.animation.enabled &&
238
+ nextConfig.animation.duration > 0 &&
227
239
  skipSignature !== sampling.requestSignature.value
228
240
  ) {
229
241
  const state = states.get(track.id)!
@@ -255,8 +267,10 @@ export function useWaveformAnimations(context: AnimationContext) {
255
267
  invalidate()
256
268
  },
257
269
  }
270
+ const generation = presentationGeneration
258
271
  controller.animateSeriesEnter(id, binding, track.width, {
259
272
  ...nextConfig.animation,
273
+ duration: pagination.enterDuration(),
260
274
  delay:
261
275
  nextConfig.animation.delay + visibleIds.indexOf(id) * nextConfig.animation.staggerDelay,
262
276
  onComplete: () => {
@@ -264,7 +278,7 @@ export function useWaveformAnimations(context: AnimationContext) {
264
278
  entering.delete(id)
265
279
  invalidate()
266
280
  queueMicrotask(() => {
267
- if (!disposed) annotations.release(id)
281
+ if (!disposed && presentationGeneration === generation) annotations.release(id)
268
282
  })
269
283
  nextConfig.animation.onComplete?.()
270
284
  },
@@ -311,6 +325,17 @@ export function useWaveformAnimations(context: AnimationContext) {
311
325
  reconcile,
312
326
  { immediate: true },
313
327
  )
328
+ watch(
329
+ [() => animationGridShape(context), () => props.data],
330
+ ([shape, data], [previousShape, previousData]) => {
331
+ previousGrid = shape
332
+ if (disposed || shape === previousShape || data !== previousData || !seen.size) return
333
+ // Lifecycle page resets have now selected the final grid and page.
334
+ pagination.requestLayout()
335
+ reconcile()
336
+ },
337
+ { flush: 'post' },
338
+ )
314
339
  watch(
315
340
  () => props.annotationsVisible,
316
341
  (visible) => {
@@ -343,7 +368,6 @@ export function useWaveformAnimations(context: AnimationContext) {
343
368
  controller.finishWhere(
344
369
  (key) => key.scope === 'update' || key.scope === 'domain',
345
370
  () => {
346
- pagination.reset()
347
371
  for (const track of tracks.value) {
348
372
  const old = states.get(track.id),
349
373
  next = staticTrack(track, visibleAnnotations(), old, true)
@@ -405,7 +429,6 @@ export function useWaveformAnimations(context: AnimationContext) {
405
429
  return {
406
430
  presentationTracks,
407
431
  presentationAnnotations,
408
- paginationOpacity: pagination.opacity,
409
432
  stabilizeAnimations,
410
433
  wrap: <T extends object>(commands: T) => wrapAnimationCommands(commands, stabilizeAnimations),
411
434
  wrapPagination: pagination.wrapPagination,
@@ -422,7 +422,6 @@ export function useWaveformChartController(
422
422
  ...animations.wrap(commands),
423
423
  presentationTracks: animations.presentationTracks,
424
424
  presentationAnnotations: animations.presentationAnnotations,
425
- paginationOpacity: animations.paginationOpacity,
426
425
  stabilizeAnimations: animations.stabilizeAnimations,
427
426
  ...imageExport,
428
427
  margin,
@@ -7,7 +7,6 @@ import { waveformLineDasharray, waveformPointSeriesPath } from './seriesStyle'
7
7
 
8
8
  const props = defineProps<{
9
9
  track: TrackLayout
10
- paginationOpacity?: number
11
10
  clipPathId: string
12
11
  }>()
13
12
 
@@ -53,7 +52,7 @@ const renderedSeriesPaths = computed<RenderedSeriesPath[]>(() =>
53
52
  <g
54
53
  v-for="seriesPath in renderedSeriesPaths"
55
54
  :key="seriesPath.presentationKey ?? seriesPath.series.id"
56
- :opacity="(seriesPath.opacity ?? 1) * (paginationOpacity ?? 1)"
55
+ :opacity="seriesPath.opacity ?? 1"
57
56
  :clip-path="seriesPath.animationClip ? `url(#${seriesPath.animationClip.id})` : undefined"
58
57
  class="waveform-track__series-item waveform-chart__series"
59
58
  :data-series-id="seriesPath.series.id"
@@ -12,7 +12,6 @@ import WaveformTrackBackdrop from './WaveformTrackBackdrop.vue'
12
12
 
13
13
  interface Props {
14
14
  track: TrackLayout
15
- paginationOpacity?: number
16
15
  clipPathId: string
17
16
  innerWidth: number
18
17
  zoomable: boolean
@@ -137,11 +136,7 @@ const frameInset = computed(() =>
137
136
  aria-hidden="true"
138
137
  />
139
138
 
140
- <WaveformSeriesLayer
141
- :track="track"
142
- :clip-path-id="clipPathId"
143
- :pagination-opacity="paginationOpacity"
144
- />
139
+ <WaveformSeriesLayer :track="track" :clip-path-id="clipPathId" />
145
140
  <WaveformZeroLines
146
141
  :track="track"
147
142
  :clip-path-id="clipPathId"
@@ -13,6 +13,7 @@ const enter = ref(true)
13
13
  const update = ref(true)
14
14
  const annotationEnabled = ref(true)
15
15
  const effect = ref<AnnotationAnimationType>('scale')
16
+ const gridShape = ref('1x1')
16
17
  const direction = ref<LineDrawingDirection>('left-to-right')
17
18
  let generation = 0
18
19
  function generate(count = 500): WaveformData {
@@ -55,13 +56,13 @@ function toggleAnnotation() {
55
56
  <main class="animation-demo">
56
57
  <h1>波形动画</h1>
57
58
  <p>
58
- 分别控制入场、更新 /
59
- 切页和标注效果。切换分页时波形与标注同步淡入,坐标轴立即更新。拖动、缩放与导出会提交最终画面。
59
+ 分别控制入场、更新和标注效果。分页与网格调整重播波形绘制,标注等待对应系列完成,
60
+ 坐标轴立即更新。拖动、缩放与导出会提交最终画面。
60
61
  </p>
61
62
  <div class="animation-demo__controls">
62
63
  <label><input v-model="enabled" type="checkbox" /> 动画</label>
63
- <label><input v-model="enter" type="checkbox" /> 入场</label>
64
- <label><input v-model="update" type="checkbox" /> 更新 / 切页</label>
64
+ <label><input v-model="enter" type="checkbox" /> 入场 / 切页 / 网格</label>
65
+ <label><input v-model="update" type="checkbox" /> 更新</label>
65
66
  <label><input v-model="annotationEnabled" type="checkbox" /> 标注</label>
66
67
  <label
67
68
  >入场方向
@@ -80,6 +81,14 @@ function toggleAnnotation() {
80
81
  <option value="bounce">弹跳</option>
81
82
  </select></label
82
83
  >
84
+ <label
85
+ >网格
86
+ <select v-model="gridShape" aria-label="网格">
87
+ <option value="1x1">1×1</option>
88
+ <option value="1x2">1×2</option>
89
+ <option value="2x2">2×2</option>
90
+ </select>
91
+ </label>
83
92
  <button @click="key++">重新入场</button>
84
93
  <button @click="replace(true)">更新振幅</button>
85
94
  <button @click="replace(false)">改变点数</button>
@@ -89,7 +98,7 @@ function toggleAnnotation() {
89
98
  :key="key"
90
99
  :data="data"
91
100
  :height="360"
92
- :grid="{ rowCount: 1, columnCount: 1 }"
101
+ :grid="{ rowCount: Number(gridShape[0]), columnCount: Number(gridShape[2]) }"
93
102
  :animation-enabled="enabled"
94
103
  :animation="{ enabled: enter, direction }"
95
104
  :animation-update="{ enabled: update }"