FPGA_WebLab/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue

248 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="w-full"
:class="{
'h-48': !analyzer.logicData.value,
'h-150': analyzer.logicData.value,
}"
>
<v-chart
v-if="analyzer.logicData.value"
class="w-full h-full"
:option="option"
autoresize
:update-options="updateOptions"
/>
<div
v-else
class="w-full h-full flex flex-col gap-6 items-center justify-center"
>
<div class="text-center">
<h3 class="text-xl font-semibold text-slate-600 mb-2">
暂无逻辑分析数据
</h3>
<p class="text-sm text-slate-500">点击下方按钮生成测试数据用于观察</p>
</div>
<button
class="group relative px-8 py-3 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 ease-in-out focus:outline-none focus:ring-4 focus:ring-blue-300 active:scale-95"
@click="analyzer.generateTestData"
>
<span class="flex items-center gap-2">
<RefreshCcw
class="w-5 h-5 group-hover:rotate-180 transition-transform duration-300"
/>
生成测试数据
</span>
<div
class="absolute inset-0 bg-white opacity-0 group-hover:opacity-20 rounded-lg transition-opacity duration-200"
></div>
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, shallowRef } from "vue";
import VChart from "vue-echarts";
import { RefreshCcw } from "lucide-vue-next";
// Echarts
import { use } from "echarts/core";
import { LineChart } from "echarts/charts";
import {
TooltipComponent,
GridComponent,
DataZoomComponent,
AxisPointerComponent,
ToolboxComponent,
} from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import type { ComposeOption } from "echarts/core";
import type { LineSeriesOption } from "echarts/charts";
import type {
AxisPointerComponentOption,
TooltipComponentOption,
GridComponentOption,
DataZoomComponentOption,
} from "echarts/components";
import type {
ToolboxComponentOption,
XAXisOption,
YAXisOption,
} from "echarts/types/dist/shared";
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
import { useRequiredInjection } from "@/utils/Common";
import { isUndefined } from "lodash";
use([
TooltipComponent,
ToolboxComponent,
GridComponent,
AxisPointerComponent,
DataZoomComponent,
LineChart,
CanvasRenderer,
]);
type EChartsOption = ComposeOption<
| AxisPointerComponentOption
| TooltipComponentOption
| ToolboxComponentOption
| GridComponentOption
| DataZoomComponentOption
| LineSeriesOption
>;
const analyzer = useRequiredInjection(useLogicAnalyzerState);
// 添加更新选项来减少重绘
const updateOptions = shallowRef({
notMerge: false,
lazyUpdate: true,
silent: false,
});
const option = computed((): EChartsOption => {
if (isUndefined(analyzer.logicData.value)) return {};
const channelCount = analyzer.logicData.value.y.length;
const channelSpacing = 2; // 每个通道之间的间距
// 使用单个网格
const grids: GridComponentOption[] = [
{
left: "5%",
right: "5%",
top: "5%",
bottom: "15%",
},
];
// 单个X轴
const xAxis: XAXisOption[] = [
{
type: "category",
boundaryGap: false,
data: analyzer.logicData.value.x.map((x) => x.toFixed(3)),
axisLabel: {
formatter: (value: string) =>
analyzer.logicData.value
? `${value}${analyzer.logicData.value.xUnit}`
: `${value}`,
},
},
];
// 单个Y轴范围根据通道数量调整
const yAxis: YAXisOption[] = [
{
type: "value",
min: -0.5,
max: channelCount * channelSpacing - 0.5,
interval: channelSpacing,
axisLabel: {
formatter: (value: number) => {
const channelIndex = Math.round(value / channelSpacing);
return channelIndex < channelCount
? analyzer.channelNames.value[channelIndex]
: "";
},
},
splitLine: { show: false },
},
];
// 创建系列数据每个通道有不同的Y偏移
const series: LineSeriesOption[] = analyzer.logicData.value.y.map(
(channelData: number[], index: number) => ({
name: analyzer.channelNames.value[index],
type: "line",
data: channelData.map(
(value: number) => value + index * channelSpacing + 0.2,
),
step: "end",
lineStyle: {
width: 2,
},
areaStyle: {
opacity: 0.3,
origin: index * channelSpacing,
},
symbol: "none",
// 优化性能配置
sampling: "lttb",
// large: true,
// largeThreshold: 2000,
// progressive: 2000,
// 减少动画以避免闪烁
animation: false,
}),
);
return {
// 全局动画配置
animation: false,
tooltip: {
trigger: "axis",
axisPointer: {
type: "line",
label: {
backgroundColor: "#6a7985",
},
// 减少axisPointer的动画
animation: false,
},
formatter: (params: any) => {
if (Array.isArray(params) && params.length > 0) {
const timeValue = analyzer.logicData.value!.x[params[0].dataIndex];
const dataIndex = params[0].dataIndex;
let tooltip = `Time: ${timeValue.toFixed(3)}${analyzer.logicData.value!.xUnit}<br/>`;
// 显示所有通道在当前时间点的原始数值0或1
if (analyzer.logicData.value) {
analyzer.channelNames.value.forEach(
(channelName: string, index: number) => {
const originalValue =
analyzer.logicData.value!.y[index][dataIndex];
tooltip += `${channelName}: ${originalValue}<br/>`;
},
);
}
return tooltip;
}
return "";
},
// 优化tooltip性能
hideDelay: 100,
},
toolbox: {
feature: {
restore: {},
},
},
grid: grids,
xAxis: xAxis,
yAxis: yAxis,
dataZoom: [
{
show: true,
realtime: true,
start: 0,
end: 100,
},
{
type: "inside",
realtime: true,
start: 0,
end: 100,
},
],
series: series,
};
});
</script>