feat(chart): add axis and dotted frame controls
This commit is contained in:
29
README.md
29
README.md
@@ -108,6 +108,7 @@ const data = ref<WaveformData>({
|
||||
| `initialXDomain` | `[number, number]` | 未设置 | 所有图框的初始 X 范围 |
|
||||
| `initialXDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置初始范围 |
|
||||
| `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 |
|
||||
| `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线显隐 |
|
||||
| `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 |
|
||||
| `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 |
|
||||
| `frameNumber` | `string \| number` | 未设置 | 图框水印内容 |
|
||||
@@ -121,7 +122,8 @@ const data = ref<WaveformData>({
|
||||
|
||||
所有公开类型均可从包入口导入,例如 `WaveformData`、`WaveformSeries`、
|
||||
`WaveformAnnotation`、`WaveformLineStyle`、`WaveformRenderingOptions`、
|
||||
`WaveformZeroLineOptions`、`WaveformGridOptions` 和 `WaveformGridTrackLines`。
|
||||
`WaveformAxesOptions`、`WaveformZeroLineOptions`、`WaveformGridOptions` 和
|
||||
`WaveformGridTrackLines`。
|
||||
|
||||
### 数据结构
|
||||
|
||||
@@ -340,6 +342,8 @@ Demo 左侧控制面板提供标题实时预览,可配置标题名称、显隐
|
||||
/>
|
||||
```
|
||||
|
||||
`frameStyle.borderStyle` 支持 `solid`(实线)、`dashed`(虚线)和 `dotted`(点虚线)。
|
||||
|
||||
对应的公开类型为 `WaveformFrameStyle`。默认边框颜色为 `#1f2937`、线宽为 `1`、线型为
|
||||
`solid`,背景透明。`borderWidth` 为 `0` 时隐藏边框;非有限值或负数会回退到默认线宽。
|
||||
|
||||
@@ -441,6 +445,29 @@ tooltip 仍然可用,切换回普通模式后原有配置和标注不会丢失
|
||||
/>
|
||||
```
|
||||
|
||||
`axes` 可以分别隐藏 X/Y 轴的基线,同时保留刻度短线、刻度数字、科学计数倍率、单位和轴标题。
|
||||
与关闭网格线、设置 `frameStyle` 组合后,可以只使用图框边框围住绘图区:
|
||||
|
||||
```vue
|
||||
<WaveformChart
|
||||
:data="chartData"
|
||||
:axes="{
|
||||
x: { lineVisible: false },
|
||||
y: { lineVisible: false },
|
||||
}"
|
||||
:grid="{
|
||||
trackLines: {
|
||||
voltage: { horizontal: false, vertical: false },
|
||||
},
|
||||
}"
|
||||
:frame-style="{
|
||||
borderColor: '#1f2937',
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
}"
|
||||
/>
|
||||
```
|
||||
|
||||
`interactionMode` 可选 `zoom` 或 `annotation`,默认使用缩放模式。右键绘图区可直接打开
|
||||
标注编辑器,无需切换交互模式。`zoomable`、`pannable` 和 `showTooltip` 可分别控制缩放、
|
||||
空格拖拽平移和 tooltip;平移默认关闭。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "waveform-analysis",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
|
||||
@@ -46,6 +46,8 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
|
||||
expect(gridSizeInputs[1]?.props('value')).toBe(1)
|
||||
expect(panel.find('[aria-label="显示水平网格线"]').exists()).toBe(true)
|
||||
expect(panel.find('[aria-label="显示垂直网格线"]').exists()).toBe(true)
|
||||
expect(panel.find('[aria-label="显示横轴线"]').exists()).toBe(true)
|
||||
expect(panel.find('[aria-label="显示纵轴线"]').exists()).toBe(true)
|
||||
expect(panel.find('[aria-label="水平网格线颜色"]').exists()).toBe(true)
|
||||
expect(panel.find('[aria-label="垂直网格线颜色"]').exists()).toBe(true)
|
||||
expect(panel.find('[aria-label="净图模式"]').exists()).toBe(true)
|
||||
@@ -62,6 +64,9 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
|
||||
expect(frameControls.text()).toContain('背景颜色')
|
||||
expect(frameControls.find('[aria-label="图框线宽"]').exists()).toBe(true)
|
||||
expect(frameControls.find('[aria-label="图框线型"]').exists()).toBe(true)
|
||||
expect(
|
||||
frameControls.get('[aria-label="图框线型"]').getComponent(Select).props('options'),
|
||||
).toContainEqual({ label: '点虚线', value: 'dotted' })
|
||||
expect(frameControls.find('[aria-label="显示图框水印"]').exists()).toBe(true)
|
||||
expect(frameControls.get('.frame-style-control--switch .ant-switch').classes()).toContain(
|
||||
'ant-switch-small',
|
||||
@@ -129,6 +134,27 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('passes independent X and Y axis-line controls to the chart', async () => {
|
||||
const wrapper = mount(App)
|
||||
await flushPromises()
|
||||
const chart = wrapper.getComponent(WaveformChart)
|
||||
|
||||
expect(chart.props('axes')).toEqual({
|
||||
x: { lineVisible: true },
|
||||
y: { lineVisible: true },
|
||||
})
|
||||
|
||||
await wrapper.get('[aria-label="显示横轴线"]').trigger('click')
|
||||
await wrapper.get('[aria-label="显示纵轴线"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(chart.props('axes')).toEqual({
|
||||
x: { lineVisible: false },
|
||||
y: { lineVisible: false },
|
||||
})
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('passes tooltip and per-series line-style controls to the chart', async () => {
|
||||
const wrapper = mount(App)
|
||||
await flushPromises()
|
||||
|
||||
21
src/App.vue
21
src/App.vue
@@ -7,6 +7,7 @@ import 'vue3-colorpicker/style.css'
|
||||
import {
|
||||
WaveformChart,
|
||||
type WaveformAnnotation,
|
||||
type WaveformAxesOptions,
|
||||
type WaveformData,
|
||||
type WaveformDisplayMode,
|
||||
type WaveformFrameStyle,
|
||||
@@ -60,13 +61,15 @@ const rowCount = ref(4)
|
||||
const columnCount = ref(1)
|
||||
const frameBorderColor = ref('#1f2937')
|
||||
const frameBorderWidth = ref(1)
|
||||
const frameBorderStyle = ref<'solid' | 'dashed'>('solid')
|
||||
const frameBorderStyle = ref<NonNullable<WaveformFrameStyle['borderStyle']>>('solid')
|
||||
const frameBackgroundColor = ref('rgba(255, 255, 255, 0)')
|
||||
const frameWatermarkVisible = ref(true)
|
||||
const horizontalGridVisible = ref(true)
|
||||
const horizontalGridColor = ref('#dfe5ef')
|
||||
const verticalGridVisible = ref(true)
|
||||
const verticalGridColor = ref('#dfe5ef')
|
||||
const xAxisLineVisible = ref(true)
|
||||
const yAxisLineVisible = ref(true)
|
||||
const annotations = ref<WaveformAnnotation[]>([])
|
||||
const annotationsVisible = ref(true)
|
||||
const cleanView = ref(false)
|
||||
@@ -109,6 +112,7 @@ const legendOrientationOptions: Array<{ label: string; value: WaveformLegendOrie
|
||||
const frameBorderStyleOptions = [
|
||||
{ label: '实线', value: 'solid' },
|
||||
{ label: '虚线', value: 'dashed' },
|
||||
{ label: '点虚线', value: 'dotted' },
|
||||
]
|
||||
const zeroLineDashOptions = [
|
||||
{ label: '虚线', value: '6 4' },
|
||||
@@ -136,6 +140,10 @@ const frameStyle = computed<WaveformFrameStyle>(() => ({
|
||||
borderStyle: frameBorderStyle.value,
|
||||
backgroundColor: frameBackgroundColor.value,
|
||||
}))
|
||||
const axes = computed<WaveformAxesOptions>(() => ({
|
||||
x: { lineVisible: xAxisLineVisible.value },
|
||||
y: { lineVisible: yAxisLineVisible.value },
|
||||
}))
|
||||
const zeroLine = computed<WaveformZeroLineOptions>(() => ({
|
||||
visible: zeroLineVisible.value,
|
||||
color: zeroLineColor.value,
|
||||
@@ -510,7 +518,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleWindowKeydown)
|
||||
</section>
|
||||
|
||||
<section class="control-section">
|
||||
<h2>网格线</h2>
|
||||
<h2>网格与轴线</h2>
|
||||
<div class="grid-line-controls">
|
||||
<div class="grid-line-control">
|
||||
<span>水平网格</span>
|
||||
@@ -548,6 +556,14 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleWindowKeydown)
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid-line-control grid-line-control--switch-only">
|
||||
<span>横轴线</span>
|
||||
<Switch v-model:checked="xAxisLineVisible" size="small" aria-label="显示横轴线" />
|
||||
</div>
|
||||
<div class="grid-line-control grid-line-control--switch-only">
|
||||
<span>纵轴线</span>
|
||||
<Switch v-model:checked="yAxisLineVisible" size="small" aria-label="显示纵轴线" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -773,6 +789,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleWindowKeydown)
|
||||
interactive: true,
|
||||
}"
|
||||
:frame-style="frameStyle"
|
||||
:axes="axes"
|
||||
:clean-view="cleanView"
|
||||
:show-tooltip="showTooltip"
|
||||
:zero-line="zeroLine"
|
||||
|
||||
@@ -349,6 +349,75 @@ describe('WaveformChart', () => {
|
||||
expect(zeroLines[0].attributes('y1')).not.toBe(zeroLines[1].attributes('y1'))
|
||||
})
|
||||
|
||||
it('hides X and Y axis lines independently while preserving axis text and the frame', async () => {
|
||||
const wrapper = await mountSizedChart({
|
||||
kind: 'series',
|
||||
series: [
|
||||
{
|
||||
id: 'channel',
|
||||
name: 'Voltage',
|
||||
data: {
|
||||
kind: 'points',
|
||||
points: [
|
||||
{ x: 0, y: -1 },
|
||||
{ x: 1, y: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
const xAxis = wrapper.get('.waveform-chart__axis--x')
|
||||
const yAxis = wrapper.get('.waveform-chart__axis--y')
|
||||
|
||||
expect(xAxis.classes()).not.toContain('waveform-track__axis--line-hidden')
|
||||
expect(yAxis.classes()).not.toContain('waveform-track__axis--line-hidden')
|
||||
|
||||
await wrapper.setProps({ axes: { x: { lineVisible: false } } })
|
||||
await flushPromises()
|
||||
expect(xAxis.classes()).toContain('waveform-track__axis--line-hidden')
|
||||
expect(yAxis.classes()).not.toContain('waveform-track__axis--line-hidden')
|
||||
expect(xAxis.get('path.domain').attributes('display')).toBe('none')
|
||||
expect(
|
||||
xAxis.findAll('.tick line').every((line) => line.attributes('display') === undefined),
|
||||
).toBe(true)
|
||||
expect(yAxis.get('path.domain').attributes('display')).toBeUndefined()
|
||||
|
||||
await wrapper.setProps({
|
||||
axes: {
|
||||
x: { lineVisible: false },
|
||||
y: { lineVisible: false },
|
||||
},
|
||||
grid: {
|
||||
rowCount: 1,
|
||||
columnCount: 1,
|
||||
trackLines: { channel: { horizontal: false, vertical: false } },
|
||||
},
|
||||
frameStyle: { borderColor: '#dc2626', borderWidth: 2 },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
expect(xAxis.classes()).toContain('waveform-track__axis--line-hidden')
|
||||
expect(yAxis.classes()).toContain('waveform-track__axis--line-hidden')
|
||||
expect(yAxis.get('path.domain').attributes('display')).toBe('none')
|
||||
expect(
|
||||
yAxis.findAll('.tick line').every((line) => line.attributes('display') === undefined),
|
||||
).toBe(true)
|
||||
expect(xAxis.findAll('text').length).toBeGreaterThan(0)
|
||||
expect(yAxis.findAll('text').length).toBeGreaterThan(0)
|
||||
expect(wrapper.findAll('.waveform-chart__axis-endpoint')).toHaveLength(2)
|
||||
expect(wrapper.get('.waveform-chart__y-axis-label').text()).toBe('Voltage')
|
||||
expect(wrapper.findAll('[data-grid-direction]')).toHaveLength(0)
|
||||
expect(wrapper.get('.waveform-chart__plot-frame').attributes()).toMatchObject({
|
||||
stroke: '#dc2626',
|
||||
'stroke-width': '2',
|
||||
})
|
||||
|
||||
await wrapper.setProps({ axes: { x: { lineVisible: true }, y: { lineVisible: true } } })
|
||||
await flushPromises()
|
||||
expect(xAxis.get('path.domain').attributes('display')).toBeUndefined()
|
||||
expect(yAxis.get('path.domain').attributes('display')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('preserves a titled multi-axis plot and hides auxiliary layers in clean view', async () => {
|
||||
const data: WaveformData = {
|
||||
kind: 'series',
|
||||
@@ -2384,6 +2453,17 @@ describe('WaveformChart', () => {
|
||||
expect(wrapper.get('.waveform-chart__plot-frame').attributes('stroke-width')).toBe('1')
|
||||
})
|
||||
|
||||
it('renders a dotted frame with rounded dots', async () => {
|
||||
const wrapper = await mountSizedChart(gridSeries(1), {
|
||||
frameStyle: { borderStyle: 'dotted' },
|
||||
})
|
||||
|
||||
expect(wrapper.get('.waveform-chart__plot-frame').attributes()).toMatchObject({
|
||||
'stroke-dasharray': '1 3',
|
||||
'stroke-linecap': 'round',
|
||||
})
|
||||
})
|
||||
|
||||
it('continues minor x-grid lines beyond the final major tick to the exact endpoint', async () => {
|
||||
const wrapper = await mountSizedChart({
|
||||
kind: 'points',
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
|
||||
import {
|
||||
type WaveformAnnotation,
|
||||
type WaveformAxesOptions,
|
||||
type WaveformData,
|
||||
type WaveformDisplayMode,
|
||||
type WaveformFrameStyle,
|
||||
@@ -121,6 +122,7 @@ const props = withDefaults(
|
||||
timeUnit?: 's' | 'ms'
|
||||
frameNumber?: string | number
|
||||
frameStyle?: WaveformFrameStyle
|
||||
axes?: WaveformAxesOptions
|
||||
annotations?: WaveformAnnotation[]
|
||||
annotationsVisible?: boolean
|
||||
interactionMode?: WaveformInteractionMode
|
||||
@@ -251,7 +253,9 @@ function handleInteractionKeyDown(event: KeyboardEvent) {
|
||||
const target = event.target
|
||||
if (
|
||||
target instanceof Element &&
|
||||
target.closest('button, input, select, textarea, [contenteditable]:not([contenteditable="false"])')
|
||||
target.closest(
|
||||
'button, input, select, textarea, [contenteditable]:not([contenteditable="false"])',
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -296,7 +300,10 @@ const resolvedZeroLine = computed(() => {
|
||||
return {
|
||||
visible: props.zeroLine?.visible === true,
|
||||
color: props.zeroLine?.color || ZERO_LINE_DEFAULTS.COLOR,
|
||||
width: typeof width === 'number' && Number.isFinite(width) && width > 0 ? width : ZERO_LINE_DEFAULTS.WIDTH,
|
||||
width:
|
||||
typeof width === 'number' && Number.isFinite(width) && width > 0
|
||||
? width
|
||||
: ZERO_LINE_DEFAULTS.WIDTH,
|
||||
dash: props.zeroLine?.dash ?? ZERO_LINE_DEFAULTS.DASH,
|
||||
}
|
||||
})
|
||||
@@ -329,7 +336,9 @@ const titleAreaReserved = computed(
|
||||
const titleVisible = computed(() => titleAreaReserved.value && !isCleanView.value)
|
||||
const titleFontSize = computed(() => {
|
||||
const fontSize = props.title?.textStyle?.fontSize
|
||||
return Number.isFinite(fontSize) && (fontSize ?? 0) > 0 ? (fontSize as number) : TITLE_DEFAULT_FONT_SIZE
|
||||
return Number.isFinite(fontSize) && (fontSize ?? 0) > 0
|
||||
? (fontSize as number)
|
||||
: TITLE_DEFAULT_FONT_SIZE
|
||||
})
|
||||
const titleRotation = computed(() => {
|
||||
const rotation = props.title?.textStyle?.rotation
|
||||
@@ -354,7 +363,10 @@ const estimatedTitleWidth = computed(() => {
|
||||
const spacingWidth = Number.isFinite(letterSpacing)
|
||||
? Math.max(0, resolvedTitleText.value.length - 1) * letterSpacing
|
||||
: 0
|
||||
return Math.max(1, resolvedTitleText.value.length * titleFontSize.value * TITLE_CHAR_WIDTH_RATIO + spacingWidth)
|
||||
return Math.max(
|
||||
1,
|
||||
resolvedTitleText.value.length * titleFontSize.value * TITLE_CHAR_WIDTH_RATIO + spacingWidth,
|
||||
)
|
||||
})
|
||||
const titleAvailableWidth = computed(() => {
|
||||
const measuredAvailableWidth = chartWidth.value - TITLE_AREA_HORIZONTAL_PADDING * 2
|
||||
@@ -866,10 +878,14 @@ function clearZoomBindings() {
|
||||
|
||||
function resolveMaximumZoomScale(domain: [number, number]): number {
|
||||
const minZoomSpan = props.minZoomSpan
|
||||
if (!Number.isFinite(minZoomSpan) || (minZoomSpan ?? 0) <= 0) return ZOOM_CONSTRAINTS.DEFAULT_MAX_SCALE
|
||||
if (!Number.isFinite(minZoomSpan) || (minZoomSpan ?? 0) <= 0)
|
||||
return ZOOM_CONSTRAINTS.DEFAULT_MAX_SCALE
|
||||
const domainSpan = Math.abs(domain[1] - domain[0])
|
||||
if (!Number.isFinite(domainSpan) || domainSpan <= 0) return ZOOM_CONSTRAINTS.MIN_SCALE
|
||||
return Math.min(ZOOM_CONSTRAINTS.DEFAULT_MAX_SCALE, Math.max(ZOOM_CONSTRAINTS.MIN_SCALE, domainSpan / (minZoomSpan ?? domainSpan)))
|
||||
return Math.min(
|
||||
ZOOM_CONSTRAINTS.DEFAULT_MAX_SCALE,
|
||||
Math.max(ZOOM_CONSTRAINTS.MIN_SCALE, domainSpan / (minZoomSpan ?? domainSpan)),
|
||||
)
|
||||
}
|
||||
|
||||
function canZoomTrack(track: TrackLayout): boolean {
|
||||
@@ -1929,6 +1945,7 @@ onBeforeUnmount(() => {
|
||||
:interaction-mode="activeInteractionMode"
|
||||
:frame-number="resolveFrameNumber(track.index)"
|
||||
:frame-style="frameStyle"
|
||||
:axes="axes"
|
||||
:clean-view="isCleanView"
|
||||
:zero-line="resolvedZeroLine"
|
||||
:time-unit="timeUnit"
|
||||
|
||||
@@ -19,6 +19,7 @@ export type {
|
||||
WaveformLegendOrientation,
|
||||
WaveformLegendOptions,
|
||||
WaveformFrameStyle,
|
||||
WaveformAxesOptions,
|
||||
WaveformZeroLineOptions,
|
||||
SingleWaveformData,
|
||||
WaveformLineType,
|
||||
|
||||
@@ -17,6 +17,7 @@ export type {
|
||||
WaveformLegendOrientation,
|
||||
WaveformLegendOptions,
|
||||
WaveformFrameStyle,
|
||||
WaveformAxesOptions,
|
||||
WaveformZeroLineOptions,
|
||||
WaveformPoint,
|
||||
WaveformSeries,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { axisBottom, axisLeft, axisRight, select } from 'd3'
|
||||
import { formatAxisTime, formatScientificAxisLabel } from '../../utils'
|
||||
import type { WaveformFrameStyle, WaveformZeroLineOptions } from '../../types'
|
||||
import type { WaveformAxesOptions, WaveformFrameStyle, WaveformZeroLineOptions } from '../../types'
|
||||
import type { WaveformDisplayMode, WaveformInteractionMode } from '../data/types'
|
||||
import type {
|
||||
DisplaySeries,
|
||||
@@ -31,6 +31,8 @@ interface Props {
|
||||
frameNumber?: string | number
|
||||
/** 图框样式 */
|
||||
frameStyle?: WaveformFrameStyle
|
||||
/** 坐标轴线显示选项 */
|
||||
axes?: WaveformAxesOptions
|
||||
/** 时间单位 */
|
||||
timeUnit: 's' | 'ms'
|
||||
/** 悬浮点(用于显示十字线) */
|
||||
@@ -72,7 +74,10 @@ const resolvedFrameStyle = computed(() => {
|
||||
typeof borderWidth === 'number' && Number.isFinite(borderWidth) && borderWidth >= 0
|
||||
? borderWidth
|
||||
: 1,
|
||||
borderStyle: props.frameStyle?.borderStyle === 'dashed' ? 'dashed' : 'solid',
|
||||
borderStyle:
|
||||
props.frameStyle?.borderStyle === 'dashed' || props.frameStyle?.borderStyle === 'dotted'
|
||||
? props.frameStyle.borderStyle
|
||||
: 'solid',
|
||||
backgroundColor: props.frameStyle?.backgroundColor || 'transparent',
|
||||
}
|
||||
})
|
||||
@@ -138,11 +143,16 @@ function renderAxes() {
|
||||
|
||||
yAxis.tickValues(axis.tickValues)
|
||||
|
||||
select(element).call(yAxis)
|
||||
const selection = select(element)
|
||||
selection.call(yAxis)
|
||||
selection
|
||||
.selectAll('path.domain')
|
||||
.attr('display', props.axes?.y?.lineVisible === false ? 'none' : null)
|
||||
})
|
||||
|
||||
if (xAxisElement.value) {
|
||||
select(xAxisElement.value).call(
|
||||
const selection = select(xAxisElement.value)
|
||||
selection.call(
|
||||
axisBottom(props.track.xScale)
|
||||
.tickValues(props.track.xAxisTickValues)
|
||||
.tickFormat((value) =>
|
||||
@@ -156,6 +166,9 @@ function renderAxes() {
|
||||
.tickPadding(7)
|
||||
.tickSizeOuter(0),
|
||||
)
|
||||
selection
|
||||
.selectAll('path.domain')
|
||||
.attr('display', props.axes?.x?.lineVisible === false ? 'none' : null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +184,8 @@ watch(
|
||||
() => props.track.xAxisTickValues,
|
||||
() => props.track.yAxisTickValues,
|
||||
() => props.timeUnit,
|
||||
() => props.axes?.x?.lineVisible,
|
||||
() => props.axes?.y?.lineVisible,
|
||||
],
|
||||
async () => {
|
||||
await nextTick()
|
||||
@@ -307,6 +322,7 @@ watch(
|
||||
v-if="track.showXAxis && !cleanView"
|
||||
ref="xAxisElement"
|
||||
class="waveform-track__axis waveform-track__axis--x waveform-chart__axis waveform-chart__axis--x"
|
||||
:class="{ 'waveform-track__axis--line-hidden': axes?.x?.lineVisible === false }"
|
||||
:transform="`translate(0, ${track.height})`"
|
||||
/>
|
||||
<g
|
||||
@@ -353,7 +369,10 @@ watch(
|
||||
:key="`y-axis-${track.index}-${axis.index}`"
|
||||
:ref="(element) => setYAxisElement(element, axis.index)"
|
||||
class="waveform-track__axis waveform-track__axis--y waveform-chart__axis waveform-chart__axis--y"
|
||||
:class="`waveform-track__axis--${axis.side}`"
|
||||
:class="[
|
||||
`waveform-track__axis--${axis.side}`,
|
||||
{ 'waveform-track__axis--line-hidden': axes?.y?.lineVisible === false },
|
||||
]"
|
||||
:data-y-axis-index="axis.index"
|
||||
:data-y-axis-side="axis.side"
|
||||
:transform="`translate(${axis.x}, 0)`"
|
||||
@@ -439,7 +458,14 @@ watch(
|
||||
fill="none"
|
||||
:stroke="resolvedFrameStyle.borderColor"
|
||||
:stroke-width="resolvedFrameStyle.borderWidth"
|
||||
:stroke-dasharray="resolvedFrameStyle.borderStyle === 'dashed' ? '6 4' : undefined"
|
||||
:stroke-dasharray="
|
||||
resolvedFrameStyle.borderStyle === 'dashed'
|
||||
? '6 4'
|
||||
: resolvedFrameStyle.borderStyle === 'dotted'
|
||||
? '1 3'
|
||||
: undefined
|
||||
"
|
||||
:stroke-linecap="resolvedFrameStyle.borderStyle === 'dotted' ? 'round' : undefined"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ export type {
|
||||
WaveformLegendOrientation,
|
||||
WaveformLegendOptions,
|
||||
WaveformFrameStyle,
|
||||
WaveformAxesOptions,
|
||||
WaveformZeroLineOptions,
|
||||
// 数据类型
|
||||
SingleWaveformData,
|
||||
|
||||
@@ -130,6 +130,10 @@ body {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.grid-line-control--switch-only {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.grid-line-color-picker,
|
||||
.grid-line-color-picker .vc-color-wrap {
|
||||
width: 48px;
|
||||
|
||||
@@ -117,10 +117,20 @@ export interface WaveformLegendOptions {
|
||||
export interface WaveformFrameStyle {
|
||||
borderColor?: string
|
||||
borderWidth?: number
|
||||
borderStyle?: 'solid' | 'dashed'
|
||||
borderStyle?: 'solid' | 'dashed' | 'dotted'
|
||||
backgroundColor?: string
|
||||
}
|
||||
|
||||
/** Controls axis baseline visibility while preserving tick marks and axis text. */
|
||||
export interface WaveformAxesOptions {
|
||||
x?: {
|
||||
lineVisible?: boolean
|
||||
}
|
||||
y?: {
|
||||
lineVisible?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Styling and visibility options for the horizontal zero-value reference line. */
|
||||
export interface WaveformZeroLineOptions {
|
||||
visible?: boolean
|
||||
|
||||
@@ -18,6 +18,7 @@ export type {
|
||||
WaveformLegendOrientation,
|
||||
WaveformLegendOptions,
|
||||
WaveformFrameStyle,
|
||||
WaveformAxesOptions,
|
||||
WaveformZeroLineOptions,
|
||||
} from './chart'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user