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.
Files changed (55) hide show
  1. package/README.md +33 -4
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +724 -621
  4. package/dist/style.css +1 -1
  5. package/dist/types/components/WaveformChart.vue.d.ts +3 -2
  6. package/dist/types/components/core/constants.d.ts +3 -1
  7. package/dist/types/components/core/trackLayoutBuilder.d.ts +2 -0
  8. package/dist/types/components/core/types.d.ts +2 -1
  9. package/dist/types/components/core/useWaveformChartController.d.ts +3 -0
  10. package/dist/types/components/core/useWaveformPresentation.d.ts +2 -0
  11. package/dist/types/components/core/waveformChartTypes.d.ts +3 -1
  12. package/dist/types/components/core/yAxisConstants.d.ts +1 -1
  13. package/dist/types/components/core/yAxisTitleLayout.d.ts +4 -0
  14. package/dist/types/components/interaction/integerZoom.d.ts +12 -0
  15. package/dist/types/components/interaction/viewportDomain.d.ts +1 -0
  16. package/dist/types/components/interaction/zoomConstraints.d.ts +2 -1
  17. package/dist/types/components/rendering/WaveformTrack.vue.d.ts +2 -6
  18. package/dist/types/components/rendering/WaveformTrackBackdrop.vue.d.ts +0 -4
  19. package/dist/types/components/rendering/WaveformZeroLines.vue.d.ts +12 -0
  20. package/dist/types/demo/types.d.ts +3 -0
  21. package/dist/types/index.d.ts +1 -0
  22. package/dist/types/types/chart.d.ts +8 -0
  23. package/package.json +1 -1
  24. package/src/App.vue +8 -2
  25. package/src/components/WaveformChart.css +4 -0
  26. package/src/components/WaveformChart.vue +2 -1
  27. package/src/components/core/constants.ts +5 -3
  28. package/src/components/core/layout.ts +1 -1
  29. package/src/components/core/trackLayoutBuilder.ts +30 -19
  30. package/src/components/core/types.ts +2 -0
  31. package/src/components/core/useWaveformChartLifecycle.ts +1 -1
  32. package/src/components/core/useWaveformLayout.ts +34 -17
  33. package/src/components/core/useWaveformPresentation.ts +11 -1
  34. package/src/components/core/waveformChartTypes.ts +3 -0
  35. package/src/components/core/yAxisConstants.ts +1 -1
  36. package/src/components/core/yAxisTitleLayout.ts +78 -0
  37. package/src/components/interaction/integerZoom.ts +64 -0
  38. package/src/components/interaction/useWaveformViewport.ts +8 -12
  39. package/src/components/interaction/useWaveformZoom.ts +6 -4
  40. package/src/components/interaction/viewportDomain.ts +12 -0
  41. package/src/components/interaction/zoomConstraints.ts +7 -5
  42. package/src/components/rendering/WaveformLegend.vue +1 -1
  43. package/src/components/rendering/WaveformSeriesLayer.vue +1 -1
  44. package/src/components/rendering/WaveformTrack.css +5 -2
  45. package/src/components/rendering/WaveformTrack.vue +18 -5
  46. package/src/components/rendering/WaveformTrackAxes.vue +9 -3
  47. package/src/components/rendering/WaveformTrackBackdrop.vue +2 -34
  48. package/src/components/rendering/WaveformZeroLines.vue +53 -0
  49. package/src/demo/DemoChartHost.vue +1 -0
  50. package/src/demo/DemoGridFrameControls.vue +12 -0
  51. package/src/demo/DemoViewControls.vue +4 -0
  52. package/src/demo/types.ts +3 -0
  53. package/src/index.ts +1 -0
  54. package/src/styles.css +3 -3
  55. 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<Pick<WaveformZeroLineOptions, 'color' | 'width' | 'dash'>> & {
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: () => ({ visible: false, color: '#98a2b3', width: 1, dash: '6 4' }),
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.yAxisLabelX - Y_AXIS_LABEL_BAND_WIDTH / 2"
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.yAxisLabelX}, ${track.height / 2}) rotate(-90)`"
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 { WaveformZeroLineOptions } from '../../types'
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
- const props = defineProps<Props>()
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
@@ -5,6 +5,7 @@
5
5
 
6
6
  // Vue 组件
7
7
  export { default as WaveformChart } from './components/WaveformChart.vue'
8
+ export type { WaveformChartProps } from './components/core/waveformChartTypes'
8
9
 
9
10
  // 类型定义
10
11
  export type {
package/src/styles.css CHANGED
@@ -329,7 +329,7 @@ body {
329
329
 
330
330
  .mobile-control-toggle {
331
331
  position: fixed;
332
- top: 10px;
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: 24;
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: 30;
354
+ z-index: 50;
355
355
  width: min(300px, calc(100vw - 48px));
356
356
  visibility: hidden;
357
357
  border-radius: 0 6px 6px 0;
@@ -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
  }