waveform-analysis 0.1.64 → 0.1.66
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 +33 -4
- package/dist/index.cjs +1 -1
- package/dist/index.js +724 -621
- package/dist/style.css +1 -1
- package/dist/types/components/WaveformChart.vue.d.ts +3 -2
- package/dist/types/components/core/constants.d.ts +3 -1
- package/dist/types/components/core/trackLayoutBuilder.d.ts +2 -0
- package/dist/types/components/core/types.d.ts +2 -1
- package/dist/types/components/core/useWaveformChartController.d.ts +3 -0
- package/dist/types/components/core/useWaveformPresentation.d.ts +2 -0
- package/dist/types/components/core/waveformChartTypes.d.ts +3 -1
- package/dist/types/components/core/yAxisConstants.d.ts +1 -1
- package/dist/types/components/core/yAxisTitleLayout.d.ts +4 -0
- package/dist/types/components/interaction/integerZoom.d.ts +12 -0
- package/dist/types/components/interaction/viewportDomain.d.ts +1 -0
- package/dist/types/components/interaction/zoomConstraints.d.ts +2 -1
- package/dist/types/components/rendering/WaveformTrack.vue.d.ts +2 -6
- package/dist/types/components/rendering/WaveformTrackBackdrop.vue.d.ts +0 -4
- package/dist/types/components/rendering/WaveformZeroLines.vue.d.ts +12 -0
- package/dist/types/demo/types.d.ts +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/types/chart.d.ts +8 -0
- package/package.json +1 -1
- package/src/App.vue +8 -2
- package/src/components/WaveformChart.css +4 -0
- package/src/components/WaveformChart.vue +2 -1
- package/src/components/core/constants.ts +5 -3
- package/src/components/core/layout.ts +1 -1
- package/src/components/core/trackLayoutBuilder.ts +30 -19
- package/src/components/core/types.ts +2 -0
- package/src/components/core/useWaveformChartLifecycle.ts +1 -1
- package/src/components/core/useWaveformLayout.ts +34 -17
- package/src/components/core/useWaveformPresentation.ts +11 -1
- package/src/components/core/waveformChartTypes.ts +3 -0
- package/src/components/core/yAxisConstants.ts +1 -1
- package/src/components/core/yAxisTitleLayout.ts +78 -0
- package/src/components/interaction/integerZoom.ts +64 -0
- package/src/components/interaction/useWaveformViewport.ts +8 -12
- package/src/components/interaction/useWaveformZoom.ts +6 -4
- package/src/components/interaction/viewportDomain.ts +12 -0
- package/src/components/interaction/zoomConstraints.ts +7 -5
- package/src/components/rendering/WaveformLegend.vue +1 -1
- package/src/components/rendering/WaveformSeriesLayer.vue +1 -1
- package/src/components/rendering/WaveformTrack.css +5 -2
- package/src/components/rendering/WaveformTrack.vue +18 -5
- package/src/components/rendering/WaveformTrackAxes.vue +9 -3
- package/src/components/rendering/WaveformTrackBackdrop.vue +2 -34
- package/src/components/rendering/WaveformZeroLines.vue +53 -0
- package/src/demo/DemoChartHost.vue +1 -0
- package/src/demo/DemoGridFrameControls.vue +12 -0
- package/src/demo/DemoViewControls.vue +4 -0
- package/src/demo/types.ts +3 -0
- package/src/index.ts +1 -0
- package/src/styles.css +3 -3
- package/src/types/chart.ts +8 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
|
|
4
|
+
import { ZERO_LINE_DEFAULTS } from '../core/constants'
|
|
4
5
|
import type { WaveformAxesOptions, WaveformFrameStyle, WaveformZeroLineOptions } from '../../types'
|
|
5
6
|
import type { TrackLayout } from '../core/types'
|
|
6
7
|
import type { WaveformDisplayMode, WaveformInteractionMode } from '../data/types'
|
|
7
8
|
import WaveformSeriesLayer from './WaveformSeriesLayer.vue'
|
|
9
|
+
import WaveformZeroLines from './WaveformZeroLines.vue'
|
|
8
10
|
import WaveformTrackAxes from './WaveformTrackAxes.vue'
|
|
9
11
|
import WaveformTrackBackdrop from './WaveformTrackBackdrop.vue'
|
|
10
12
|
|
|
@@ -22,9 +24,7 @@ interface Props {
|
|
|
22
24
|
timeUnit: 's' | 'ms'
|
|
23
25
|
yLabel?: string
|
|
24
26
|
cleanView?: boolean
|
|
25
|
-
zeroLine?: Required<
|
|
26
|
-
visible: boolean
|
|
27
|
-
}
|
|
27
|
+
zeroLine?: Required<WaveformZeroLineOptions>
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
interface Emits {
|
|
@@ -41,7 +41,14 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
41
41
|
interactionMode: 'zoom',
|
|
42
42
|
interactive: true,
|
|
43
43
|
cleanView: false,
|
|
44
|
-
zeroLine: () => ({
|
|
44
|
+
zeroLine: () => ({
|
|
45
|
+
visible: true,
|
|
46
|
+
color: ZERO_LINE_DEFAULTS.COLOR,
|
|
47
|
+
width: ZERO_LINE_DEFAULTS.WIDTH,
|
|
48
|
+
dash: ZERO_LINE_DEFAULTS.DASH,
|
|
49
|
+
opacity: ZERO_LINE_DEFAULTS.OPACITY,
|
|
50
|
+
boundaryThreshold: ZERO_LINE_DEFAULTS.BOUNDARY_THRESHOLD,
|
|
51
|
+
}),
|
|
45
52
|
})
|
|
46
53
|
const emit = defineEmits<Emits>()
|
|
47
54
|
|
|
@@ -83,7 +90,6 @@ const resolvedFrameStyle = computed(() => {
|
|
|
83
90
|
:clean-view="cleanView"
|
|
84
91
|
:frame-number="frameNumber"
|
|
85
92
|
:frame-style="resolvedFrameStyle"
|
|
86
|
-
:zero-line="zeroLine"
|
|
87
93
|
/>
|
|
88
94
|
<WaveformTrackAxes
|
|
89
95
|
:track="track"
|
|
@@ -114,6 +120,13 @@ const resolvedFrameStyle = computed(() => {
|
|
|
114
120
|
/>
|
|
115
121
|
|
|
116
122
|
<WaveformSeriesLayer :track="track" :clip-path-id="clipPathId" />
|
|
123
|
+
<WaveformZeroLines
|
|
124
|
+
:track="track"
|
|
125
|
+
:clip-path-id="clipPathId"
|
|
126
|
+
:inner-width="innerWidth"
|
|
127
|
+
:clean-view="cleanView"
|
|
128
|
+
:zero-line="zeroLine"
|
|
129
|
+
/>
|
|
117
130
|
|
|
118
131
|
<rect
|
|
119
132
|
v-if="!track.isEmpty && track.hasVisibleSeries && displayMode === 'independent'"
|
|
@@ -61,6 +61,12 @@ function renderAxes() {
|
|
|
61
61
|
|
|
62
62
|
const selection = select(element)
|
|
63
63
|
selection.call(yAxis)
|
|
64
|
+
const lowestTick = Math.min(...axis.tickValues)
|
|
65
|
+
selection
|
|
66
|
+
.selectAll<SVGTextElement, number>('.tick text')
|
|
67
|
+
.attr('y', null)
|
|
68
|
+
.attr('dy', (value) => (value === lowestTick ? '0' : '0.32em'))
|
|
69
|
+
.attr('dominant-baseline', (value) => (value === lowestTick ? 'text-after-edge' : null))
|
|
64
70
|
selection
|
|
65
71
|
.selectAll('path.domain')
|
|
66
72
|
.attr('display', props.axes?.y?.lineVisible === false ? 'none' : null)
|
|
@@ -77,7 +83,7 @@ function renderAxes() {
|
|
|
77
83
|
props.track.xScale.domain() as [number, number],
|
|
78
84
|
props.timeUnit,
|
|
79
85
|
'tick',
|
|
80
|
-
props.axes?.x?.labelFormatter,
|
|
86
|
+
props.axes?.x?.labelFormatter ?? props.track.xAxisLabelFormatter,
|
|
81
87
|
),
|
|
82
88
|
)
|
|
83
89
|
.tickSize(-4)
|
|
@@ -175,7 +181,7 @@ watch(
|
|
|
175
181
|
>
|
|
176
182
|
<rect
|
|
177
183
|
class="waveform-track__y-axis-label-bg waveform-chart__y-axis-label-bg"
|
|
178
|
-
:x="track.
|
|
184
|
+
:x="track.yAxes[0].labelX - Y_AXIS_LABEL_BAND_WIDTH / 2"
|
|
179
185
|
:y="track.height / 2 - 40"
|
|
180
186
|
:width="Y_AXIS_LABEL_BAND_WIDTH"
|
|
181
187
|
height="80"
|
|
@@ -184,7 +190,7 @@ watch(
|
|
|
184
190
|
<text
|
|
185
191
|
class="waveform-track__y-axis-label waveform-chart__y-axis-label"
|
|
186
192
|
:fill="track.series?.color"
|
|
187
|
-
:transform="`translate(${track.
|
|
193
|
+
:transform="`translate(${track.yAxes[0].labelX}, ${track.height / 2}) rotate(-90)`"
|
|
188
194
|
text-anchor="middle"
|
|
189
195
|
dominant-baseline="central"
|
|
190
196
|
>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import type { TrackLayout, WaveformYAxisLayout } from '../core/types'
|
|
2
|
+
import type { TrackLayout } from '../core/types'
|
|
4
3
|
|
|
5
4
|
interface Props {
|
|
6
5
|
track: TrackLayout
|
|
@@ -11,18 +10,9 @@ interface Props {
|
|
|
11
10
|
frameStyle: {
|
|
12
11
|
backgroundColor: string
|
|
13
12
|
}
|
|
14
|
-
zeroLine: Required<Pick<WaveformZeroLineOptions, 'color' | 'width' | 'dash'>> & {
|
|
15
|
-
visible: boolean
|
|
16
|
-
}
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function zeroLineY(axis: WaveformYAxisLayout): number | null {
|
|
22
|
-
const [minimum, maximum] = axis.scale.domain()
|
|
23
|
-
if (!props.zeroLine.visible || minimum > 0 || maximum < 0) return null
|
|
24
|
-
return axis.scale(0)
|
|
25
|
-
}
|
|
15
|
+
defineProps<Props>()
|
|
26
16
|
</script>
|
|
27
17
|
|
|
28
18
|
<template>
|
|
@@ -98,28 +88,6 @@ function zeroLineY(axis: WaveformYAxisLayout): number | null {
|
|
|
98
88
|
</g>
|
|
99
89
|
</g>
|
|
100
90
|
|
|
101
|
-
<g
|
|
102
|
-
v-if="!track.isEmpty && track.hasVisibleSeries && zeroLine.visible && !cleanView"
|
|
103
|
-
class="waveform-track__zero-lines waveform-chart__zero-lines"
|
|
104
|
-
:clip-path="`url(#${clipPathId}-${track.index})`"
|
|
105
|
-
aria-hidden="true"
|
|
106
|
-
>
|
|
107
|
-
<template v-for="axis in track.yAxes" :key="`zero-line-${track.index}-${axis.index}`">
|
|
108
|
-
<line
|
|
109
|
-
v-if="zeroLineY(axis) !== null"
|
|
110
|
-
class="waveform-track__zero-line waveform-chart__zero-line"
|
|
111
|
-
:data-y-axis-index="axis.index"
|
|
112
|
-
x1="0"
|
|
113
|
-
:x2="track.width ?? innerWidth"
|
|
114
|
-
:y1="zeroLineY(axis) ?? 0"
|
|
115
|
-
:y2="zeroLineY(axis) ?? 0"
|
|
116
|
-
:stroke="zeroLine.color"
|
|
117
|
-
:stroke-width="zeroLine.width"
|
|
118
|
-
:stroke-dasharray="zeroLine.dash || undefined"
|
|
119
|
-
/>
|
|
120
|
-
</template>
|
|
121
|
-
</g>
|
|
122
|
-
|
|
123
91
|
<text
|
|
124
92
|
v-if="!track.isEmpty && track.hasVisibleSeries && frameNumber !== undefined && !cleanView"
|
|
125
93
|
class="waveform-track__watermark waveform-chart__watermark"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { WaveformZeroLineOptions } from '../../types'
|
|
3
|
+
import type { TrackLayout, WaveformYAxisLayout } from '../core/types'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
track: TrackLayout
|
|
7
|
+
clipPathId: string
|
|
8
|
+
innerWidth: number
|
|
9
|
+
cleanView: boolean
|
|
10
|
+
zeroLine: Required<WaveformZeroLineOptions>
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
function zeroLineY(axis: WaveformYAxisLayout): number | null {
|
|
14
|
+
const [minimum, maximum] = axis.scale.domain()
|
|
15
|
+
const tickInterval = Math.abs(axis.tickValues[1] - axis.tickValues[0])
|
|
16
|
+
const margin = tickInterval * props.zeroLine.boundaryThreshold
|
|
17
|
+
if (
|
|
18
|
+
!props.zeroLine.visible ||
|
|
19
|
+
minimum >= 0 ||
|
|
20
|
+
maximum <= 0 ||
|
|
21
|
+
-minimum < margin ||
|
|
22
|
+
maximum < margin
|
|
23
|
+
)
|
|
24
|
+
return null
|
|
25
|
+
return axis.scale(0)
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<g
|
|
31
|
+
v-if="!track.isEmpty && track.hasVisibleSeries && zeroLine.visible && !cleanView"
|
|
32
|
+
class="waveform-track__zero-lines waveform-chart__zero-lines"
|
|
33
|
+
:clip-path="`url(#${clipPathId}-${track.index})`"
|
|
34
|
+
aria-hidden="true"
|
|
35
|
+
pointer-events="none"
|
|
36
|
+
>
|
|
37
|
+
<template v-for="axis in track.yAxes" :key="`zero-line-${track.index}-${axis.index}`">
|
|
38
|
+
<line
|
|
39
|
+
v-if="zeroLineY(axis) !== null"
|
|
40
|
+
class="waveform-track__zero-line waveform-chart__zero-line"
|
|
41
|
+
:data-y-axis-index="axis.index"
|
|
42
|
+
x1="0"
|
|
43
|
+
:x2="track.width ?? innerWidth"
|
|
44
|
+
:y1="zeroLineY(axis) ?? 0"
|
|
45
|
+
:y2="zeroLineY(axis) ?? 0"
|
|
46
|
+
:stroke="zeroLine.color"
|
|
47
|
+
:stroke-opacity="zeroLine.opacity"
|
|
48
|
+
:stroke-width="zeroLine.width"
|
|
49
|
+
:stroke-dasharray="zeroLine.dash || undefined"
|
|
50
|
+
/>
|
|
51
|
+
</template>
|
|
52
|
+
</g>
|
|
53
|
+
</template>
|
|
@@ -23,6 +23,7 @@ defineExpose({ resetViewport })
|
|
|
23
23
|
v-model:hidden-series-ids="model.hiddenSeriesIds"
|
|
24
24
|
:data="model.data"
|
|
25
25
|
:min-visible-points="2"
|
|
26
|
+
:integer-zoom="model.integerZoom"
|
|
26
27
|
:initial-x-domain="model.initialXDomain"
|
|
27
28
|
:x-domain-strategy="{ type: 'integer-ms', includeExplicit: true }"
|
|
28
29
|
:display-mode="model.displayMode"
|
|
@@ -66,6 +66,18 @@ const model = defineModel<DemoControlPanelModel>('model', { required: true })
|
|
|
66
66
|
aria-label="零值参考线线宽"
|
|
67
67
|
/>
|
|
68
68
|
</label>
|
|
69
|
+
<label class="frame-style-control">
|
|
70
|
+
<span>透明度</span>
|
|
71
|
+
<InputNumber
|
|
72
|
+
v-model:value="model.zeroLineOpacity"
|
|
73
|
+
:min="0"
|
|
74
|
+
:max="1"
|
|
75
|
+
:step="0.05"
|
|
76
|
+
:precision="2"
|
|
77
|
+
size="small"
|
|
78
|
+
aria-label="零值参考线透明度"
|
|
79
|
+
/>
|
|
80
|
+
</label>
|
|
69
81
|
<label class="frame-style-control">
|
|
70
82
|
<span>线型</span>
|
|
71
83
|
<Select
|
|
@@ -38,6 +38,10 @@ const model = defineModel<DemoControlPanelModel>('model', { required: true })
|
|
|
38
38
|
<h2>视图</h2>
|
|
39
39
|
<Button block aria-label="重置波形视图" @click="model.resetWaveformViewport"> 重置视图 </Button>
|
|
40
40
|
<div class="auxiliary-style-controls" style="margin-top: 10px">
|
|
41
|
+
<label class="frame-style-control frame-style-control--switch">
|
|
42
|
+
<span>整数缩放</span>
|
|
43
|
+
<Switch v-model:checked="model.integerZoom" size="small" aria-label="整数缩放" />
|
|
44
|
+
</label>
|
|
41
45
|
<label class="frame-style-control frame-style-control--switch">
|
|
42
46
|
<span>数值 Tooltip</span>
|
|
43
47
|
<Switch v-model:checked="model.showTooltip" size="small" aria-label="显示数值 Tooltip" />
|
package/src/demo/types.ts
CHANGED
|
@@ -27,6 +27,7 @@ export interface DemoControlPanelModel {
|
|
|
27
27
|
displayMode: WaveformDisplayMode
|
|
28
28
|
overlayMode: WaveformOverlayMode
|
|
29
29
|
showTooltip: boolean
|
|
30
|
+
integerZoom: boolean
|
|
30
31
|
plotMarginTop: number
|
|
31
32
|
plotMarginBottom: number
|
|
32
33
|
cleanView: boolean
|
|
@@ -38,6 +39,7 @@ export interface DemoControlPanelModel {
|
|
|
38
39
|
zeroLineVisible: boolean
|
|
39
40
|
zeroLineColor: string
|
|
40
41
|
zeroLineWidth: number
|
|
42
|
+
zeroLineOpacity: number
|
|
41
43
|
zeroLineDash: string
|
|
42
44
|
zeroLineDashOptions: Array<SelectOption<string>>
|
|
43
45
|
rowCount: number
|
|
@@ -101,6 +103,7 @@ export interface DemoChartModel {
|
|
|
101
103
|
cleanView: boolean
|
|
102
104
|
presentationMode: boolean
|
|
103
105
|
showTooltip: boolean
|
|
106
|
+
integerZoom: boolean
|
|
104
107
|
plotMargin: WaveformPlotMargin
|
|
105
108
|
zeroLine: WaveformZeroLineOptions
|
|
106
109
|
frameWatermarkVisible: boolean
|
package/src/index.ts
CHANGED
package/src/styles.css
CHANGED
|
@@ -329,7 +329,7 @@ body {
|
|
|
329
329
|
|
|
330
330
|
.mobile-control-toggle {
|
|
331
331
|
position: fixed;
|
|
332
|
-
top:
|
|
332
|
+
top: 58px;
|
|
333
333
|
left: 10px;
|
|
334
334
|
z-index: 20;
|
|
335
335
|
display: inline-flex;
|
|
@@ -339,7 +339,7 @@ body {
|
|
|
339
339
|
.control-backdrop {
|
|
340
340
|
position: fixed;
|
|
341
341
|
inset: 0 0 0 min(300px, calc(100vw - 48px));
|
|
342
|
-
z-index:
|
|
342
|
+
z-index: 44;
|
|
343
343
|
display: block;
|
|
344
344
|
width: auto;
|
|
345
345
|
height: 100%;
|
|
@@ -351,7 +351,7 @@ body {
|
|
|
351
351
|
.control-panel {
|
|
352
352
|
position: fixed;
|
|
353
353
|
inset: 0 auto 0 0;
|
|
354
|
-
z-index:
|
|
354
|
+
z-index: 50;
|
|
355
355
|
width: min(300px, calc(100vw - 48px));
|
|
356
356
|
visibility: hidden;
|
|
357
357
|
border-radius: 0 6px 6px 0;
|
package/src/types/chart.ts
CHANGED
|
@@ -258,8 +258,16 @@ export interface WaveformAxesOptions {
|
|
|
258
258
|
|
|
259
259
|
/** Styling and visibility options for the horizontal zero-value reference line. */
|
|
260
260
|
export interface WaveformZeroLineOptions {
|
|
261
|
+
/** Defaults to true. Set false to hide the reference line. */
|
|
261
262
|
visible?: boolean
|
|
263
|
+
/** Defaults to red (#ff0000). */
|
|
262
264
|
color?: string
|
|
265
|
+
/** Positive stroke width in pixels. Defaults to 1. */
|
|
263
266
|
width?: number
|
|
267
|
+
/** SVG stroke-dasharray. Defaults to '6 4'; use '' for a solid line. */
|
|
264
268
|
dash?: string
|
|
269
|
+
/** Stroke opacity from 0 to 1. Defaults to 0.5; multiplies the color's alpha. */
|
|
270
|
+
opacity?: number
|
|
271
|
+
/** Hide within this fraction of a Y-axis tick interval from either boundary. Defaults to 0.02. */
|
|
272
|
+
boundaryThreshold?: number
|
|
265
273
|
}
|