waveform-analysis 0.1.74 → 0.1.76
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.
- package/README.md +422 -60
- package/dist/assets/workerRuntime-BJiRmIZD.js +2446 -0
- package/dist/index.cjs +1 -2
- package/dist/index.js +1 -7119
- package/dist/style.css +1 -1
- package/dist/types/animation/WaveformAnimationController.d.ts +30 -0
- package/dist/types/animation/core/Animation.d.ts +13 -0
- package/dist/types/animation/core/Animator.d.ts +23 -0
- package/dist/types/animation/core/easing.d.ts +3 -0
- package/dist/types/animation/core/errors.d.ts +4 -0
- package/dist/types/animation/core/handle.d.ts +7 -0
- package/dist/types/animation/core/owner.d.ts +3 -0
- package/dist/types/animation/core/propertyGroups.d.ts +17 -0
- package/dist/types/animation/core/task.d.ts +29 -0
- package/dist/types/animation/core/types.d.ts +74 -0
- package/dist/types/animation/core/validation.d.ts +30 -0
- package/dist/types/animation/core/values.d.ts +8 -0
- package/dist/types/animation/presets/annotation.d.ts +4 -0
- package/dist/types/animation/presets/lineDrawing.d.ts +7 -0
- package/dist/types/animation/presets/shared.d.ts +14 -0
- package/dist/types/animation/presets/transitions.d.ts +4 -0
- package/dist/types/animation/types.d.ts +84 -0
- package/dist/types/components/WaveformChart.vue.d.ts +3 -2
- package/dist/types/components/animation/annotationPresentation.d.ts +17 -0
- package/dist/types/components/animation/chartExportVersion.d.ts +2 -0
- package/dist/types/components/animation/config.d.ts +31 -0
- package/dist/types/components/animation/context.d.ts +20 -0
- package/dist/types/components/animation/geometry.d.ts +10 -0
- package/dist/types/components/animation/paginationPresentation.d.ts +13 -0
- package/dist/types/components/animation/pathBuffer.d.ts +4 -0
- package/dist/types/components/animation/trackPresentation.d.ts +35 -0
- package/dist/types/components/animation/useWaveformAnimations.d.ts +27 -0
- package/dist/types/components/annotation/WaveformAnnotationContextMenu.vue.d.ts +2 -2
- package/dist/types/components/annotation/types.d.ts +7 -0
- package/dist/types/components/controls/useChartImageExport.d.ts +2 -0
- package/dist/types/components/core/amplitudeLimits.d.ts +6 -0
- package/dist/types/components/core/layout.d.ts +6 -9
- package/dist/types/components/core/trackLayoutBuilder.d.ts +3 -1
- package/dist/types/components/core/types.d.ts +8 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +26 -1
- package/dist/types/components/core/useWaveformData.d.ts +2 -2
- package/dist/types/components/core/useWaveformMeasurements.d.ts +4 -1
- package/dist/types/components/core/useWaveformRenderSampling.d.ts +2 -0
- package/dist/types/components/core/waveformChartTypes.d.ts +6 -3
- package/dist/types/components/core/xAxisTicks.d.ts +2 -0
- package/dist/types/components/core/yAxisLayoutMetrics.d.ts +2 -1
- package/dist/types/components/core/yAxisTicks.d.ts +6 -0
- package/dist/types/components/data/types.d.ts +1 -1
- package/dist/types/components/interaction/integerZoom.d.ts +3 -3
- package/dist/types/components/interaction/stepZoom.d.ts +5 -0
- package/dist/types/components/interaction/useWaveformViewport.d.ts +0 -3
- package/dist/types/components/interaction/useWaveformZoom.d.ts +0 -2
- package/dist/types/components/rendering/WaveformSeriesLayer.vue.d.ts +1 -0
- package/dist/types/components/rendering/WaveformTrack.vue.d.ts +4 -3
- package/dist/types/components/rendering/updateAxis.d.ts +5 -0
- package/dist/types/components/rendering/updateGrid.d.ts +3 -0
- package/dist/types/components/rendering/yAxisTickVisibility.d.ts +2 -0
- package/dist/types/components/utils/assignElement.d.ts +2 -0
- package/dist/types/core/data.d.ts +1 -1
- package/dist/types/demo/DemoChartHost.vue.d.ts +1 -1
- package/dist/types/demo/remoteWaveformLoader.d.ts +23 -0
- package/dist/types/demo/types.d.ts +11 -2
- package/dist/types/demo/useRemoteWaveformWindow.d.ts +22 -0
- package/dist/types/index.d.ts +10 -1
- package/dist/types/types/chart.d.ts +8 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/utils/formatters.d.ts +4 -4
- package/dist/types/utils/xAxisNumber.d.ts +2 -0
- package/dist/types/views/AnimationDemo.vue.d.ts +3 -0
- package/package.json +5 -2
- package/src/App.vue +34 -100
- package/src/DemoRouterApp.vue +1 -0
- package/src/animation/WaveformAnimationController.ts +328 -0
- package/src/animation/core/Animation.ts +37 -0
- package/src/animation/core/Animator.ts +200 -0
- package/src/animation/core/easing.ts +74 -0
- package/src/animation/core/errors.ts +24 -0
- package/src/animation/core/handle.ts +34 -0
- package/src/animation/core/owner.ts +9 -0
- package/src/animation/core/propertyGroups.ts +143 -0
- package/src/animation/core/task.ts +153 -0
- package/src/animation/core/types.ts +81 -0
- package/src/animation/core/validation.ts +173 -0
- package/src/animation/core/values.ts +38 -0
- package/src/animation/presets/annotation.ts +66 -0
- package/src/animation/presets/lineDrawing.ts +51 -0
- package/src/animation/presets/shared.ts +58 -0
- package/src/animation/presets/transitions.ts +77 -0
- package/src/animation/types.ts +81 -0
- package/src/components/WaveformChart.vue +1 -0
- package/src/components/WaveformChartView.vue +13 -6
- package/src/components/animation/annotationPresentation.ts +145 -0
- package/src/components/animation/chartExportVersion.ts +5 -0
- package/src/components/animation/config.ts +87 -0
- package/src/components/animation/context.ts +56 -0
- package/src/components/animation/geometry.ts +148 -0
- package/src/components/animation/paginationPresentation.ts +114 -0
- package/src/components/animation/pathBuffer.ts +61 -0
- package/src/components/animation/trackPresentation.ts +172 -0
- package/src/components/animation/useWaveformAnimations.ts +420 -0
- package/src/components/annotation/WaveformAnnotationLayer.vue +43 -30
- package/src/components/annotation/types.ts +3 -0
- package/src/components/controls/useChartCommands.ts +12 -13
- package/src/components/controls/useChartImageExport.ts +4 -0
- package/src/components/core/amplitudeLimits.ts +44 -0
- package/src/components/core/layout.ts +22 -34
- package/src/components/core/trackLayoutBuilder.ts +21 -23
- package/src/components/core/types.ts +4 -0
- package/src/components/core/useWaveformChartController.ts +27 -23
- package/src/components/core/useWaveformChartLifecycle.ts +11 -2
- package/src/components/core/useWaveformData.ts +11 -5
- package/src/components/core/useWaveformLayout.ts +9 -4
- package/src/components/core/useWaveformMeasurements.ts +3 -0
- package/src/components/core/useWaveformRenderSampling.ts +23 -1
- package/src/components/core/useWaveformYAxisLayout.ts +2 -0
- package/src/components/core/waveformChartTypes.ts +6 -2
- package/src/components/core/xAxisTicks.ts +69 -0
- package/src/components/core/yAxisLayoutMetrics.ts +4 -0
- package/src/components/core/yAxisTicks.ts +39 -0
- package/src/components/data/types.ts +2 -0
- package/src/components/interaction/integerZoom.ts +31 -12
- package/src/components/interaction/remoteViewport.ts +26 -4
- package/src/components/interaction/stepZoom.ts +33 -0
- package/src/components/interaction/useWaveformViewport.ts +3 -26
- package/src/components/interaction/useWaveformZoom.ts +50 -157
- package/src/components/interaction/zoomConstraints.ts +5 -5
- package/src/components/rendering/WaveformSeriesLayer.vue +13 -1
- package/src/components/rendering/WaveformTrack.vue +6 -1
- package/src/components/rendering/WaveformTrackAxes.vue +64 -51
- package/src/components/rendering/WaveformTrackBackdrop.vue +35 -49
- package/src/components/rendering/updateAxis.ts +57 -0
- package/src/components/rendering/updateGrid.ts +42 -0
- package/src/components/rendering/yAxisTickVisibility.ts +45 -18
- package/src/components/utils/assignElement.ts +7 -0
- package/src/core/data.ts +5 -2
- package/src/data/simulatedWaveforms.ts +0 -10
- package/src/demo/DemoChartHost.vue +49 -4
- package/src/demo/remoteWaveformLoader.ts +114 -0
- package/src/demo/types.ts +11 -1
- package/src/demo/useRemoteWaveformWindow.ts +229 -0
- package/src/index.ts +54 -0
- package/src/router.ts +1 -0
- package/src/types/chart.ts +10 -1
- package/src/types/index.ts +2 -0
- package/src/utils/formatters.ts +10 -5
- package/src/utils/xAxisNumber.ts +36 -0
- package/src/views/AnimationDemo.vue +129 -0
- package/dist/assets/workerRuntime-5p-EI-GL.js +0 -1
- package/dist/types/data/sx2Channel.d.ts +0 -10
- package/src/data/sx2Channel.ts +0 -315
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
scaleLinear,
|
|
3
|
-
select,
|
|
4
|
-
zoom,
|
|
5
|
-
zoomIdentity,
|
|
6
|
-
zoomTransform,
|
|
7
|
-
type D3ZoomEvent,
|
|
8
|
-
type ZoomBehavior,
|
|
9
|
-
type ZoomTransform,
|
|
10
|
-
} from 'd3'
|
|
1
|
+
import { pointer, scaleLinear, select, zoomIdentity, zoomTransform, type ZoomTransform } from 'd3'
|
|
11
2
|
import type { ComputedRef, ShallowRef } from 'vue'
|
|
12
3
|
|
|
13
|
-
import {
|
|
14
|
-
import { WHEEL_ZOOM_DEBOUNCE_MS, ZOOM_CONSTRAINTS } from '../core/constants'
|
|
4
|
+
import { WHEEL_ZOOM_DEBOUNCE_MS } from '../core/constants'
|
|
15
5
|
import type { TrackLayout } from '../core/types'
|
|
16
6
|
import type { ResolvedWaveformChartProps, WaveformChartEmit } from '../core/waveformChartTypes'
|
|
17
7
|
import { useAnimationFrameThrottle } from '../utils/useAnimationFrameThrottle'
|
|
18
|
-
import {
|
|
8
|
+
import { resolveStepZoomDomain, sameZoomDomain } from './stepZoom'
|
|
19
9
|
import { normalizeIntegerZoomDomain } from './integerZoom'
|
|
20
|
-
import {
|
|
10
|
+
import { transformForDomain, type ZoomSeriesGroup } from './zoomConstraints'
|
|
21
11
|
import {
|
|
22
12
|
emitIndependentWheelZoomEnds,
|
|
23
13
|
emitSharedWheelZoomEnd,
|
|
@@ -57,16 +47,13 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
57
47
|
initialXDomain,
|
|
58
48
|
sharedZoomDomain,
|
|
59
49
|
innerWidth,
|
|
60
|
-
innerHeight,
|
|
61
50
|
hasChartArea,
|
|
62
51
|
isZoomMode,
|
|
63
52
|
isPresentationMode,
|
|
64
53
|
resolveInitialTrackDomain,
|
|
65
54
|
cancelPendingHover,
|
|
66
55
|
} = context
|
|
67
|
-
const zoomBehaviors = new Map<number | 'shared', ZoomBehavior<SVGRectElement, unknown>>()
|
|
68
56
|
const zoomThrottle = useAnimationFrameThrottle()
|
|
69
|
-
let synchronizingZoomTransform = false
|
|
70
57
|
let pendingSharedZoomTransform: ZoomTransform | null = null
|
|
71
58
|
let pendingSharedZoomGesture: ZoomGestureKind | null = null
|
|
72
59
|
let lastSharedZoomGesture: ZoomGestureKind | null = null
|
|
@@ -75,11 +62,10 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
75
62
|
const lastIndependentWheelSeriesIds = new Map<string, string[]>()
|
|
76
63
|
let wheelZoomEndTimer: ReturnType<typeof setTimeout> | undefined
|
|
77
64
|
let preservePendingWheelDuringDataRebind = false
|
|
78
|
-
let zoomBindingEpoch = 0
|
|
79
65
|
|
|
80
66
|
const scheduleZoomCommit = () => zoomThrottle.schedule(commitPendingZoom)
|
|
81
|
-
const handleSharedZoom = (event:
|
|
82
|
-
if (
|
|
67
|
+
const handleSharedZoom = (event: { transform: ZoomTransform }) => {
|
|
68
|
+
if (isPresentationMode.value) return
|
|
83
69
|
cancelPendingHover()
|
|
84
70
|
pendingSharedZoomTransform = event.transform
|
|
85
71
|
pendingSharedZoomGesture = 'wheel'
|
|
@@ -91,11 +77,8 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
91
77
|
emit('zoom-intent', { start, end, gesture: 'wheel' })
|
|
92
78
|
scheduleZoomCommit()
|
|
93
79
|
}
|
|
94
|
-
const handleIndependentZoom = (
|
|
95
|
-
|
|
96
|
-
trackIndex: number,
|
|
97
|
-
) => {
|
|
98
|
-
if (synchronizingZoomTransform || isPresentationMode.value) return
|
|
80
|
+
const handleIndependentZoom = (event: { transform: ZoomTransform }, trackIndex: number) => {
|
|
81
|
+
if (isPresentationMode.value) return
|
|
99
82
|
cancelPendingHover()
|
|
100
83
|
pendingIndependentZoomTransforms.set(trackIndex, event.transform)
|
|
101
84
|
pendingIndependentZoomGestures.set(trackIndex, 'wheel')
|
|
@@ -166,16 +149,6 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
166
149
|
emitSharedWheelZoomEnd(emit, sharedZoomDomain.value, trackLayouts.value)
|
|
167
150
|
lastSharedZoomGesture = null
|
|
168
151
|
}
|
|
169
|
-
const flushPendingZoom = () => {
|
|
170
|
-
zoomThrottle.flush()
|
|
171
|
-
commitPendingZoom()
|
|
172
|
-
if (lastSharedZoomGesture === 'wheel' || lastIndependentWheelSeriesIds.size) return
|
|
173
|
-
emitZoomEnd()
|
|
174
|
-
}
|
|
175
|
-
const handleD3ZoomEnd = (bindingEpoch: number) => {
|
|
176
|
-
if (bindingEpoch !== zoomBindingEpoch) return
|
|
177
|
-
flushPendingZoom()
|
|
178
|
-
}
|
|
179
152
|
function scheduleWheelZoomEnd() {
|
|
180
153
|
if (wheelZoomEndTimer !== undefined) clearTimeout(wheelZoomEndTimer)
|
|
181
154
|
wheelZoomEndTimer = setTimeout(() => {
|
|
@@ -207,81 +180,50 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
207
180
|
}
|
|
208
181
|
}
|
|
209
182
|
const clearZoomBindings = (preservePendingWheel = false) => {
|
|
210
|
-
zoomBindingEpoch += 1
|
|
211
183
|
if (!preservePendingWheel) cancelPendingZoom()
|
|
212
184
|
svgElement.value
|
|
213
185
|
?.querySelectorAll<SVGRectElement>('.waveform-chart__overlay')
|
|
214
186
|
.forEach((overlay) => select(overlay).on('.zoom', null))
|
|
215
|
-
zoomBehaviors.clear()
|
|
216
187
|
}
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
): number => {
|
|
221
|
-
const domainSpan = Math.abs(domain[1] - domain[0])
|
|
222
|
-
if (!Number.isFinite(domainSpan) || domainSpan <= 0) return ZOOM_CONSTRAINTS.MIN_SCALE
|
|
223
|
-
const minimumSpan = resolveMinimumZoomSpan(domain, groups, props)
|
|
224
|
-
return Math.max(
|
|
225
|
-
ZOOM_CONSTRAINTS.MIN_SCALE,
|
|
226
|
-
minimumSpan > 0 ? domainSpan / minimumSpan : Number.POSITIVE_INFINITY,
|
|
227
|
-
)
|
|
228
|
-
}
|
|
229
|
-
const constrainTransform = (
|
|
230
|
-
transform: ZoomTransform,
|
|
231
|
-
domain: [number, number],
|
|
188
|
+
const bindWheelZoom = (
|
|
189
|
+
overlay: SVGRectElement,
|
|
190
|
+
baseDomain: [number, number],
|
|
232
191
|
width: number,
|
|
233
192
|
groups: readonly ZoomSeriesGroup[],
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (event.type !== 'wheel') return false
|
|
265
|
-
const deltaY = (event as WheelEvent).deltaY
|
|
266
|
-
const currentScale = zoomTransform(event.currentTarget as Element).k
|
|
267
|
-
|
|
268
|
-
// 放大操作:需要检查是否还有足够的数据点支持继续放大
|
|
269
|
-
if (deltaY < 0) return canZoomIn
|
|
270
|
-
|
|
271
|
-
// 缩小操作:只要当前缩放级别大于最小比例就允许
|
|
272
|
-
// 即使已达到最小可见点数限制,用户也应该能够缩小视图
|
|
273
|
-
if (deltaY > 0) {
|
|
274
|
-
// 使用小的容差避免浮点精度问题导致无法缩小到边界
|
|
275
|
-
const scaleTolerance = 1e-6
|
|
276
|
-
return currentScale > minimumScale * (1 + scaleTolerance)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return false
|
|
193
|
+
transform: ZoomTransform,
|
|
194
|
+
trackIndex?: number,
|
|
195
|
+
) => {
|
|
196
|
+
select(overlay)
|
|
197
|
+
.property('__zoom', transform)
|
|
198
|
+
.on(
|
|
199
|
+
'wheel.zoom',
|
|
200
|
+
(event: WheelEvent) => {
|
|
201
|
+
if (!Number.isFinite(event.deltaY) || event.deltaY === 0) return
|
|
202
|
+
const current = zoomTransform(overlay)
|
|
203
|
+
.rescaleX(scaleLinear(baseDomain, [0, width]))
|
|
204
|
+
.domain() as [number, number]
|
|
205
|
+
const next = resolveStepZoomDomain(
|
|
206
|
+
current,
|
|
207
|
+
event.deltaY < 0 ? 'zoom-in' : 'zoom-out',
|
|
208
|
+
baseDomain,
|
|
209
|
+
groups,
|
|
210
|
+
props,
|
|
211
|
+
pointer(event, overlay)[0] / width,
|
|
212
|
+
)
|
|
213
|
+
if (sameZoomDomain(current, next)) return
|
|
214
|
+
event.preventDefault()
|
|
215
|
+
event.stopImmediatePropagation()
|
|
216
|
+
const transform = transformForDomain(next, baseDomain, width)
|
|
217
|
+
select(overlay).property('__zoom', transform)
|
|
218
|
+
if (trackIndex === undefined) handleSharedZoom({ transform })
|
|
219
|
+
else handleIndependentZoom({ transform }, trackIndex)
|
|
220
|
+
},
|
|
221
|
+
{ passive: false },
|
|
222
|
+
)
|
|
280
223
|
}
|
|
281
224
|
const configureZoom = () => {
|
|
282
225
|
const preservePendingWheel = preservePendingWheelDuringDataRebind
|
|
283
226
|
clearZoomBindings(preservePendingWheel)
|
|
284
|
-
const bindingEpoch = zoomBindingEpoch
|
|
285
227
|
if (
|
|
286
228
|
isPresentationMode.value ||
|
|
287
229
|
!props.zoomable ||
|
|
@@ -299,35 +241,14 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
299
241
|
if (!overlay) return
|
|
300
242
|
const dataDomain = resolveInitialTrackDomain(track)
|
|
301
243
|
const groups = [track.seriesList]
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
.constrain((transform) => constrainTransform(transform, dataDomain, track.width, groups))
|
|
311
|
-
.extent([
|
|
312
|
-
[0, 0],
|
|
313
|
-
[track.width, track.height],
|
|
314
|
-
])
|
|
315
|
-
.translateExtent([
|
|
316
|
-
[0, 0],
|
|
317
|
-
[track.width, track.height],
|
|
318
|
-
])
|
|
319
|
-
.on('zoom', (event) => handleIndependentZoom(event, track.index))
|
|
320
|
-
.on('end', () => handleD3ZoomEnd(bindingEpoch))
|
|
321
|
-
zoomBehaviors.set(track.index, behavior)
|
|
322
|
-
synchronizingZoomTransform = true
|
|
323
|
-
try {
|
|
324
|
-
select(overlay)
|
|
325
|
-
.call(behavior)
|
|
326
|
-
.on('dblclick.zoom', null)
|
|
327
|
-
.call(behavior.transform, independentTransforms.value[track.index] ?? zoomIdentity)
|
|
328
|
-
} finally {
|
|
329
|
-
synchronizingZoomTransform = false
|
|
330
|
-
}
|
|
244
|
+
bindWheelZoom(
|
|
245
|
+
overlay,
|
|
246
|
+
dataDomain,
|
|
247
|
+
track.width,
|
|
248
|
+
groups,
|
|
249
|
+
independentTransforms.value[track.index] ?? zoomIdentity,
|
|
250
|
+
track.index,
|
|
251
|
+
)
|
|
331
252
|
})
|
|
332
253
|
return
|
|
333
254
|
}
|
|
@@ -336,40 +257,12 @@ export function useWaveformZoom(context: ZoomContext) {
|
|
|
336
257
|
const groups = trackLayouts.value
|
|
337
258
|
.filter((track) => track.hasVisibleSeries)
|
|
338
259
|
.map((track) => track.seriesList)
|
|
339
|
-
|
|
340
|
-
const behavior = zoom<SVGRectElement, unknown>()
|
|
341
|
-
.filter((event) => canHandleWheelZoom(event, canZoomSharedTracks(), minimumScale))
|
|
342
|
-
.scaleExtent([minimumScale, resolveMaximumZoomScale(initialXDomain.value, groups)])
|
|
343
|
-
.constrain((transform) =>
|
|
344
|
-
constrainTransform(transform, initialXDomain.value, innerWidth.value, groups),
|
|
345
|
-
)
|
|
346
|
-
.extent([
|
|
347
|
-
[0, 0],
|
|
348
|
-
[innerWidth.value, innerHeight.value],
|
|
349
|
-
])
|
|
350
|
-
.translateExtent([
|
|
351
|
-
[0, 0],
|
|
352
|
-
[innerWidth.value, innerHeight.value],
|
|
353
|
-
])
|
|
354
|
-
.on('zoom', handleSharedZoom)
|
|
355
|
-
.on('end', () => handleD3ZoomEnd(bindingEpoch))
|
|
356
|
-
zoomBehaviors.set('shared', behavior)
|
|
357
|
-
synchronizingZoomTransform = true
|
|
358
|
-
try {
|
|
359
|
-
select(overlay)
|
|
360
|
-
.call(behavior)
|
|
361
|
-
.on('dblclick.zoom', null)
|
|
362
|
-
.call(behavior.transform, sharedTransform.value)
|
|
363
|
-
} finally {
|
|
364
|
-
synchronizingZoomTransform = false
|
|
365
|
-
}
|
|
260
|
+
bindWheelZoom(overlay, initialXDomain.value, innerWidth.value, groups, sharedTransform.value)
|
|
366
261
|
}
|
|
367
262
|
|
|
368
263
|
return {
|
|
369
264
|
cancelPendingZoom,
|
|
370
265
|
clearZoomBindings,
|
|
371
|
-
canZoomTrack,
|
|
372
|
-
canZoomSharedTracks,
|
|
373
266
|
configureZoom,
|
|
374
267
|
prepareForDataChange,
|
|
375
268
|
}
|
|
@@ -73,7 +73,7 @@ export function resolveMinimumZoomSpan(
|
|
|
73
73
|
groups: readonly ZoomSeriesGroup[],
|
|
74
74
|
options: ZoomConstraintOptions,
|
|
75
75
|
): number {
|
|
76
|
-
const normalized =
|
|
76
|
+
const normalized = normalizedBoundary(boundary)
|
|
77
77
|
const boundarySpan = normalized[1] - normalized[0]
|
|
78
78
|
if (!Number.isFinite(boundarySpan) || boundarySpan <= 0) return 0
|
|
79
79
|
const configuredSpan =
|
|
@@ -89,8 +89,7 @@ export function resolveMinimumZoomSpan(
|
|
|
89
89
|
: options.maxZoomScale !== null && configuredSpan === 0 && required === 0
|
|
90
90
|
? boundarySpan / ZOOM_CONSTRAINTS.DEFAULT_MAX_SCALE
|
|
91
91
|
: 0
|
|
92
|
-
|
|
93
|
-
return Math.max(configuredSpan, pointSpan, scaleSpan, integerSpan)
|
|
92
|
+
return Math.max(configuredSpan, pointSpan, scaleSpan)
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
function expandToMinimumPoints(
|
|
@@ -141,7 +140,7 @@ export function constrainZoomDomain(
|
|
|
141
140
|
groups: readonly ZoomSeriesGroup[],
|
|
142
141
|
options: ZoomConstraintOptions,
|
|
143
142
|
): [number, number] {
|
|
144
|
-
const normalized =
|
|
143
|
+
const normalized = normalizedBoundary(boundary)
|
|
145
144
|
const boundarySpan = normalized[1] - normalized[0]
|
|
146
145
|
if (!Number.isFinite(boundarySpan) || boundarySpan <= 0) return normalized
|
|
147
146
|
if (Math.abs(domain[1] - domain[0]) >= boundarySpan * (1 - 1e-12)) return normalized
|
|
@@ -159,7 +158,8 @@ export function constrainZoomDomain(
|
|
|
159
158
|
const span = Math.max(minimumSpan, Math.min(boundarySpan, expanded[1] - expanded[0]))
|
|
160
159
|
const center = (expanded[0] + expanded[1]) / 2
|
|
161
160
|
const start = Math.max(normalized[0], Math.min(center - span / 2, normalized[1] - span))
|
|
162
|
-
|
|
161
|
+
const aligned = alignIntegerZoomDomain([start, start + span], options)
|
|
162
|
+
return [Math.max(normalized[0], aligned[0]), Math.min(normalized[1], aligned[1])]
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export function transformForDomain(
|
|
@@ -7,6 +7,7 @@ import { waveformLineDasharray, waveformPointSeriesPath } from './seriesStyle'
|
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{
|
|
9
9
|
track: TrackLayout
|
|
10
|
+
paginationOpacity?: number
|
|
10
11
|
clipPathId: string
|
|
11
12
|
}>()
|
|
12
13
|
|
|
@@ -51,11 +52,22 @@ const renderedSeriesPaths = computed<RenderedSeriesPath[]>(() =>
|
|
|
51
52
|
>
|
|
52
53
|
<g
|
|
53
54
|
v-for="seriesPath in renderedSeriesPaths"
|
|
54
|
-
:key="seriesPath.series.id"
|
|
55
|
+
:key="seriesPath.presentationKey ?? seriesPath.series.id"
|
|
56
|
+
:opacity="(seriesPath.opacity ?? 1) * (paginationOpacity ?? 1)"
|
|
57
|
+
:clip-path="seriesPath.animationClip ? `url(#${seriesPath.animationClip.id})` : undefined"
|
|
55
58
|
class="waveform-track__series-item waveform-chart__series"
|
|
56
59
|
:data-series-id="seriesPath.series.id"
|
|
57
60
|
:data-series-name="seriesPath.series.name || undefined"
|
|
58
61
|
>
|
|
62
|
+
<defs v-if="seriesPath.animationClip">
|
|
63
|
+
<clipPath :id="seriesPath.animationClip.id" clipPathUnits="userSpaceOnUse">
|
|
64
|
+
<rect
|
|
65
|
+
:x="seriesPath.animationClip.x"
|
|
66
|
+
:width="seriesPath.animationClip.width"
|
|
67
|
+
:height="track.height"
|
|
68
|
+
/>
|
|
69
|
+
</clipPath>
|
|
70
|
+
</defs>
|
|
59
71
|
<path
|
|
60
72
|
v-if="seriesPath.path"
|
|
61
73
|
class="waveform-track__line waveform-chart__line"
|
|
@@ -12,6 +12,7 @@ import WaveformTrackBackdrop from './WaveformTrackBackdrop.vue'
|
|
|
12
12
|
|
|
13
13
|
interface Props {
|
|
14
14
|
track: TrackLayout
|
|
15
|
+
paginationOpacity?: number
|
|
15
16
|
clipPathId: string
|
|
16
17
|
innerWidth: number
|
|
17
18
|
zoomable: boolean
|
|
@@ -136,7 +137,11 @@ const frameInset = computed(() =>
|
|
|
136
137
|
aria-hidden="true"
|
|
137
138
|
/>
|
|
138
139
|
|
|
139
|
-
<WaveformSeriesLayer
|
|
140
|
+
<WaveformSeriesLayer
|
|
141
|
+
:track="track"
|
|
142
|
+
:clip-path-id="clipPathId"
|
|
143
|
+
:pagination-opacity="paginationOpacity"
|
|
144
|
+
/>
|
|
140
145
|
<WaveformZeroLines
|
|
141
146
|
:track="track"
|
|
142
147
|
:clip-path-id="clipPathId"
|
|
@@ -6,10 +6,11 @@ import type { WaveformAxesOptions } from '../../types'
|
|
|
6
6
|
import { formatXAxisLabel } from '../../utils'
|
|
7
7
|
import { formatYAxisTickLabel } from '../core/layout'
|
|
8
8
|
import type { DisplaySeries, TrackLayout, WaveformYAxisLayout } from '../core/types'
|
|
9
|
-
import {
|
|
9
|
+
import { scheduleMaximumTickVisibility } from './yAxisTickVisibility'
|
|
10
10
|
import { sameTick } from '../core/yAxisPadding'
|
|
11
11
|
import { resolveYAxisLabelBandWidth, resolveYAxisLabelFontSize } from '../core/yAxisLabelStyle'
|
|
12
12
|
import { formatChannelLabel } from './channelLabel'
|
|
13
|
+
import { updateAxis, updateSvgAttribute } from './updateAxis'
|
|
13
14
|
|
|
14
15
|
interface Props {
|
|
15
16
|
track: TrackLayout
|
|
@@ -58,77 +59,89 @@ function renderAxes() {
|
|
|
58
59
|
if (!element) return
|
|
59
60
|
const unit = props.showUnits ? axis.seriesList[0]?.unit : undefined
|
|
60
61
|
const highestTick = Math.max(...axis.tickValues)
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
),
|
|
62
|
+
const lowestTick = Math.min(...axis.tickValues)
|
|
63
|
+
const format = (value: number) =>
|
|
64
|
+
formatYAxisTickLabel(
|
|
65
|
+
value,
|
|
66
|
+
axis.scale.domain() as [number, number],
|
|
67
|
+
axis.tickValues,
|
|
68
|
+
unit,
|
|
69
|
+
axis.side,
|
|
70
70
|
)
|
|
71
|
+
const yAxis = (axis.side === 'left' ? axisLeft(axis.scale) : axisRight(axis.scale))
|
|
72
|
+
.tickFormat((value) => format(Number(value)))
|
|
71
73
|
.tickSize(-4)
|
|
72
74
|
.tickPadding(7)
|
|
73
75
|
.tickSizeOuter(0)
|
|
74
76
|
.tickValues(axis.tickValues)
|
|
75
77
|
|
|
76
78
|
const selection = select(element)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
79
|
+
updateAxis(element, yAxis, axis.scale, axis.tickValues, axis.side, format)
|
|
80
|
+
updateSvgAttribute(element, 'data-label-domain-min', axis.scale.domain()[0])
|
|
81
|
+
updateSvgAttribute(element, 'data-label-domain-max', axis.scale.domain()[1])
|
|
82
|
+
updateSvgAttribute(element, 'data-label-unit', unit ?? null)
|
|
83
|
+
selection.selectAll<SVGGElement, number>('.tick').each(function (value) {
|
|
84
|
+
const text = this.querySelector('text')
|
|
85
|
+
if (text) {
|
|
86
|
+
updateSvgAttribute(text, 'y', null)
|
|
87
|
+
updateSvgAttribute(text, 'dy', '0')
|
|
88
|
+
updateSvgAttribute(text, 'dominant-baseline', 'central')
|
|
89
|
+
updateSvgAttribute(
|
|
90
|
+
text,
|
|
91
|
+
'data-axis-maximum-label',
|
|
92
|
+
props.hideOverlappingMaximumLabel && sameTick(value, highestTick) ? 'true' : null,
|
|
93
|
+
)
|
|
94
|
+
updateSvgAttribute(
|
|
95
|
+
text,
|
|
96
|
+
'data-axis-minimum-label',
|
|
97
|
+
props.hideOverlappingMaximumLabel && sameTick(value, lowestTick) ? 'true' : null,
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
updateSvgAttribute(
|
|
101
|
+
this,
|
|
102
|
+
'data-maximum-tick',
|
|
98
103
|
axis.maximumTick !== undefined && sameTick(value, axis.maximumTick) ? 'true' : null,
|
|
99
104
|
)
|
|
100
|
-
|
|
105
|
+
updateSvgAttribute(
|
|
106
|
+
this,
|
|
107
|
+
'data-minimum-tick',
|
|
101
108
|
axis.minimumTick !== undefined && sameTick(value, axis.minimumTick) ? 'true' : null,
|
|
102
109
|
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
updateSvgAttribute(this, 'data-tick-value', value)
|
|
111
|
+
updateSvgAttribute(this, 'data-tick-y', axis.scale(value))
|
|
112
|
+
})
|
|
113
|
+
selection.selectAll<SVGPathElement, unknown>('path.domain').each(function () {
|
|
114
|
+
updateSvgAttribute(this, 'display', props.axes?.y?.lineVisible === false ? 'none' : null)
|
|
115
|
+
})
|
|
108
116
|
})
|
|
109
117
|
|
|
110
118
|
scheduleVisibility()
|
|
111
119
|
if (!xAxisElement.value) return
|
|
112
120
|
const selection = select(xAxisElement.value)
|
|
113
|
-
|
|
121
|
+
const format = (value: number) =>
|
|
122
|
+
formatXAxisLabel(
|
|
123
|
+
value,
|
|
124
|
+
props.track.xScale.domain() as [number, number],
|
|
125
|
+
props.timeUnit,
|
|
126
|
+
'tick',
|
|
127
|
+
props.axes?.x?.labelFormatter ?? props.track.xAxisLabelFormatter,
|
|
128
|
+
)
|
|
129
|
+
updateAxis(
|
|
130
|
+
xAxisElement.value,
|
|
114
131
|
axisBottom(props.track.xScale)
|
|
115
132
|
.tickValues(props.track.xAxisTickValues)
|
|
116
|
-
.tickFormat((value) =>
|
|
117
|
-
formatXAxisLabel(
|
|
118
|
-
Number(value),
|
|
119
|
-
props.track.xScale.domain() as [number, number],
|
|
120
|
-
props.timeUnit,
|
|
121
|
-
'tick',
|
|
122
|
-
props.axes?.x?.labelFormatter ?? props.track.xAxisLabelFormatter,
|
|
123
|
-
),
|
|
124
|
-
)
|
|
133
|
+
.tickFormat((value) => format(Number(value)))
|
|
125
134
|
.tickSize(-4)
|
|
126
135
|
.tickPadding(7)
|
|
127
136
|
.tickSizeOuter(0),
|
|
137
|
+
props.track.xScale,
|
|
138
|
+
props.track.xAxisTickValues,
|
|
139
|
+
'bottom',
|
|
140
|
+
format,
|
|
128
141
|
)
|
|
129
|
-
selection
|
|
130
|
-
|
|
131
|
-
|
|
142
|
+
selection.selectAll<SVGPathElement, unknown>('path.domain').each(function () {
|
|
143
|
+
updateSvgAttribute(this, 'display', props.axes?.x?.lineVisible === false ? 'none' : null)
|
|
144
|
+
})
|
|
132
145
|
}
|
|
133
146
|
|
|
134
147
|
let disposed = false
|
|
@@ -136,7 +149,7 @@ function scheduleVisibility() {
|
|
|
136
149
|
if (disposed) return
|
|
137
150
|
const apply = () => {
|
|
138
151
|
const svg = yAxisElements.value[0]?.ownerSVGElement
|
|
139
|
-
if (svg)
|
|
152
|
+
if (svg) scheduleMaximumTickVisibility(svg)
|
|
140
153
|
}
|
|
141
154
|
apply()
|
|
142
155
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { ref, watchEffect } from 'vue'
|
|
3
|
+
import { updateGrid } from './updateGrid'
|
|
2
4
|
import type { TrackLayout } from '../core/types'
|
|
3
5
|
|
|
4
6
|
interface Props {
|
|
@@ -13,7 +15,35 @@ interface Props {
|
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
defineProps<Props>()
|
|
18
|
+
const props = defineProps<Props>()
|
|
19
|
+
const minorVertical = ref<SVGGElement>()
|
|
20
|
+
const minorHorizontal = ref<SVGGElement>()
|
|
21
|
+
const majorVertical = ref<SVGGElement>()
|
|
22
|
+
const majorHorizontal = ref<SVGGElement>()
|
|
23
|
+
watchEffect(
|
|
24
|
+
() => {
|
|
25
|
+
const track = props.track
|
|
26
|
+
for (const [element, direction, ticks] of [
|
|
27
|
+
[minorVertical.value, 'vertical', track.xMinorTicks],
|
|
28
|
+
[minorHorizontal.value, 'horizontal', track.yMinorTicks],
|
|
29
|
+
[majorVertical.value, 'vertical', track.xMajorTicks],
|
|
30
|
+
[majorHorizontal.value, 'horizontal', track.yMajorTicks],
|
|
31
|
+
] as const) {
|
|
32
|
+
if (element)
|
|
33
|
+
updateGrid(
|
|
34
|
+
element,
|
|
35
|
+
track.gridLines[direction] ? ticks : [],
|
|
36
|
+
direction === 'vertical' ? track.xScale : track.yScale,
|
|
37
|
+
direction,
|
|
38
|
+
direction === 'vertical' ? track.height : (track.width ?? props.innerWidth),
|
|
39
|
+
direction === 'vertical'
|
|
40
|
+
? track.gridLines.verticalColor
|
|
41
|
+
: track.gridLines.horizontalColor,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{ flush: 'post' },
|
|
46
|
+
)
|
|
17
47
|
</script>
|
|
18
48
|
|
|
19
49
|
<template>
|
|
@@ -34,58 +64,14 @@ defineProps<Props>()
|
|
|
34
64
|
<g
|
|
35
65
|
class="waveform-track__grid waveform-track__grid--minor waveform-chart__grid waveform-chart__grid--minor"
|
|
36
66
|
>
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
v-for="tick in track.xMinorTicks"
|
|
40
|
-
:key="`x-minor-${track.index}-${tick}`"
|
|
41
|
-
data-grid-direction="vertical"
|
|
42
|
-
:stroke="track.gridLines.verticalColor"
|
|
43
|
-
:x1="track.xScale(tick)"
|
|
44
|
-
:x2="track.xScale(tick)"
|
|
45
|
-
y1="0"
|
|
46
|
-
:y2="track.height"
|
|
47
|
-
/>
|
|
48
|
-
</template>
|
|
49
|
-
<template v-if="track.gridLines.horizontal">
|
|
50
|
-
<line
|
|
51
|
-
v-for="tick in track.yMinorTicks"
|
|
52
|
-
:key="`y-minor-${track.index}-${tick}`"
|
|
53
|
-
data-grid-direction="horizontal"
|
|
54
|
-
:stroke="track.gridLines.horizontalColor"
|
|
55
|
-
x1="0"
|
|
56
|
-
:x2="track.width ?? innerWidth"
|
|
57
|
-
:y1="track.yScale(tick)"
|
|
58
|
-
:y2="track.yScale(tick)"
|
|
59
|
-
/>
|
|
60
|
-
</template>
|
|
67
|
+
<g ref="minorVertical" />
|
|
68
|
+
<g ref="minorHorizontal" />
|
|
61
69
|
</g>
|
|
62
70
|
<g
|
|
63
71
|
class="waveform-track__grid waveform-track__grid--major waveform-chart__grid waveform-chart__grid--major"
|
|
64
72
|
>
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
v-for="tick in track.xMajorTicks"
|
|
68
|
-
:key="`x-major-${track.index}-${tick}`"
|
|
69
|
-
data-grid-direction="vertical"
|
|
70
|
-
:stroke="track.gridLines.verticalColor"
|
|
71
|
-
:x1="track.xScale(tick)"
|
|
72
|
-
:x2="track.xScale(tick)"
|
|
73
|
-
y1="0"
|
|
74
|
-
:y2="track.height"
|
|
75
|
-
/>
|
|
76
|
-
</template>
|
|
77
|
-
<template v-if="track.gridLines.horizontal">
|
|
78
|
-
<line
|
|
79
|
-
v-for="tick in track.yMajorTicks"
|
|
80
|
-
:key="`y-major-${track.index}-${tick}`"
|
|
81
|
-
data-grid-direction="horizontal"
|
|
82
|
-
:stroke="track.gridLines.horizontalColor"
|
|
83
|
-
x1="0"
|
|
84
|
-
:x2="track.width ?? innerWidth"
|
|
85
|
-
:y1="track.yScale(tick)"
|
|
86
|
-
:y2="track.yScale(tick)"
|
|
87
|
-
/>
|
|
88
|
-
</template>
|
|
73
|
+
<g ref="majorVertical" />
|
|
74
|
+
<g ref="majorHorizontal" />
|
|
89
75
|
</g>
|
|
90
76
|
</g>
|
|
91
77
|
|