Commit e898d322 by licheng

test: 高频大数据波形图验证

parent 7f91efbd
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"diagram-js": "^12.8.0", "diagram-js": "^12.8.0",
"driver.js": "^1.3.1", "driver.js": "^1.3.1",
"echarts": "^5.5.0", "echarts": "^6.0.0",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "2.9.1", "element-plus": "2.9.1",
"fast-xml-parser": "^4.3.2", "fast-xml-parser": "^4.3.2",
......
...@@ -69,11 +69,11 @@ importers: ...@@ -69,11 +69,11 @@ importers:
specifier: ^1.3.1 specifier: ^1.3.1
version: 1.3.1 version: 1.3.1
echarts: echarts:
specifier: ^5.5.0 specifier: ^6.0.0
version: 5.5.1 version: 6.0.0
echarts-wordcloud: echarts-wordcloud:
specifier: ^2.1.0 specifier: ^2.1.0
version: 2.1.0(echarts@5.5.1) version: 2.1.0(echarts@6.0.0)
element-plus: element-plus:
specifier: 2.9.1 specifier: 2.9.1
version: 2.9.1(vue@3.5.12(typescript@5.3.3)) version: 2.9.1(vue@3.5.12(typescript@5.3.3))
...@@ -3015,8 +3015,8 @@ packages: ...@@ -3015,8 +3015,8 @@ packages:
peerDependencies: peerDependencies:
echarts: ^5.0.1 echarts: ^5.0.1
echarts@5.5.1: echarts@6.0.0:
resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==} resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
ejs@3.1.10: ejs@3.1.10:
resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
...@@ -5153,8 +5153,8 @@ packages: ...@@ -5153,8 +5153,8 @@ packages:
zeebe-bpmn-moddle@1.7.0: zeebe-bpmn-moddle@1.7.0:
resolution: {integrity: sha512-eZ6OXSt0c4n9V/oN/46gTlwDIS3GhWQLt9jbM5uS/YryB4yN8wdrrKrtw+TpyNy0SSKWXNDHyC83nCA2blPO3Q==} resolution: {integrity: sha512-eZ6OXSt0c4n9V/oN/46gTlwDIS3GhWQLt9jbM5uS/YryB4yN8wdrrKrtw+TpyNy0SSKWXNDHyC83nCA2blPO3Q==}
zrender@5.6.0: zrender@6.0.0:
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==} resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
snapshots: snapshots:
...@@ -8303,14 +8303,14 @@ snapshots: ...@@ -8303,14 +8303,14 @@ snapshots:
eastasianwidth@0.2.0: {} eastasianwidth@0.2.0: {}
echarts-wordcloud@2.1.0(echarts@5.5.1): echarts-wordcloud@2.1.0(echarts@6.0.0):
dependencies: dependencies:
echarts: 5.5.1 echarts: 6.0.0
echarts@5.5.1: echarts@6.0.0:
dependencies: dependencies:
tslib: 2.3.0 tslib: 2.3.0
zrender: 5.6.0 zrender: 6.0.0
ejs@3.1.10: ejs@3.1.10:
dependencies: dependencies:
...@@ -10542,6 +10542,6 @@ snapshots: ...@@ -10542,6 +10542,6 @@ snapshots:
zeebe-bpmn-moddle@1.7.0: {} zeebe-bpmn-moddle@1.7.0: {}
zrender@5.6.0: zrender@6.0.0:
dependencies: dependencies:
tslib: 2.3.0 tslib: 2.3.0
...@@ -62,7 +62,10 @@ const styles = computed(() => { ...@@ -62,7 +62,10 @@ const styles = computed(() => {
const initChart = () => { const initChart = () => {
if (unref(elRef) && props.options) { if (unref(elRef) && props.options) {
echartRef = echarts.init(unref(elRef) as HTMLElement) echartRef = echarts.init(unref(elRef) as HTMLElement, null, {
renderer: 'canvas',
useDirtyRect: true // 启用脏矩形优化
})
echartRef?.setOption(unref(options)) echartRef?.setOption(unref(options))
} }
} }
...@@ -71,7 +74,13 @@ watch( ...@@ -71,7 +74,13 @@ watch(
() => options.value, () => options.value,
(options) => { (options) => {
if (echartRef) { if (echartRef) {
echartRef?.setOption(options) const startTime = performance.now()
echartRef.setOption({
xAxis: { data: options.xAxis.data },
series: options.series
})
const endTime = performance.now()
console.log(`ECharts 更新耗时: ${(endTime - startTime).toFixed(2)}ms`)
} }
}, },
{ {
...@@ -81,10 +90,12 @@ watch( ...@@ -81,10 +90,12 @@ watch(
const resizeHandler = debounce(() => { const resizeHandler = debounce(() => {
if (echartRef) { if (echartRef) {
const startTime = performance.now()
echartRef.resize() echartRef.resize()
const endTime = performance.now()
console.log(`ECharts 重绘耗时: ${(endTime - startTime).toFixed(2)}ms`)
} }
}, 100) }, 100)
const contentResizeHandler = async (e: TransitionEvent) => { const contentResizeHandler = async (e: TransitionEvent) => {
if (e.propertyName === 'width') { if (e.propertyName === 'width') {
resizeHandler() resizeHandler()
......
...@@ -4,24 +4,12 @@ const { t } = useI18n() ...@@ -4,24 +4,12 @@ const { t } = useI18n()
export const lineOptions: EChartsOption = { export const lineOptions: EChartsOption = {
title: { title: {
text: t('analysis.monthlySales'), text: '波形图',
left: 'center' left: 'center'
}, },
xAxis: { xAxis: {
data: [ type: 'category',
t('analysis.january'), data: [],
t('analysis.february'),
t('analysis.march'),
t('analysis.april'),
t('analysis.may'),
t('analysis.june'),
t('analysis.july'),
t('analysis.august'),
t('analysis.september'),
t('analysis.october'),
t('analysis.november'),
t('analysis.december')
],
boundaryGap: false, boundaryGap: false,
axisTick: { axisTick: {
show: false show: false
...@@ -30,7 +18,7 @@ export const lineOptions: EChartsOption = { ...@@ -30,7 +18,7 @@ export const lineOptions: EChartsOption = {
grid: { grid: {
left: 20, left: 20,
right: 20, right: 20,
bottom: 20, bottom: 50,
top: 80, top: 80,
containLabel: true containLabel: true
}, },
...@@ -42,31 +30,57 @@ export const lineOptions: EChartsOption = { ...@@ -42,31 +30,57 @@ export const lineOptions: EChartsOption = {
padding: [5, 10] padding: [5, 10]
}, },
yAxis: { yAxis: {
type: 'value',
axisTick: { axisTick: {
show: false show: false
} }
}, },
legend: { legend: {
data: [t('analysis.estimate'), t('analysis.actual')], data: ['data1'],
top: 50 top: 50
}, },
series: [ toolbox: {
feature: {
dataZoom: {
// yAxisIndex: 'none'
show: true
},
restore: {},
saveAsImage: {}
}
},
// 缩放图例(需导入 dataZoom)
dataZoom: [
{ {
name: t('analysis.estimate'), type: 'inside', // 内部缩放
smooth: true, start: 0,
type: 'line', end: 100
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
animationDuration: 2800,
animationEasing: 'cubicInOut'
}, },
{ {
name: t('analysis.actual'), start: 0,
smooth: true, end: 100
}
],
animation: false, // 禁用动画
progressive: 1000, // 每次增量渲染的数据量
progressiveThreshold: 5000, // 总数据量超过此值触发增量渲染
series: [
{
name: 'data1',
smooth: false, // 是否平滑曲线
type: 'line', type: 'line',
itemStyle: {}, showSymbol: false, // 不显示数据点
data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123], progressive: 1000, // 每次增量渲染的数据量
animationDuration: 2800, progressiveThreshold: 5000, // 总数据量超过此值触发增量渲染
animationEasing: 'quadraticOut' animation: false, // 关闭动画
lineStyle: {
width: 1 // 细线
},
sampling: 'lttb', // 使用 LTTB 采样算法
// samplingThreshold: 10000, // 采样阈值,超过此值将进行采样
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123]
// animationDuration: 2800, // 动画持续时间
// animationEasing: 'cubicInOut' // 动画缓动效果
} }
] ]
} }
......
<template>
<el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" :rows="4" animated>
<Echart :height="350" :options="lineOptionsData" />
</el-skeleton>
</el-card>
</template>
<script lang="ts" setup>
import { set } from 'lodash-es'
import { EChartsOption } from 'echarts'
import 'echarts/lib/component/dataZoom'
import { lineOptions } from './echarts-data'
defineOptions({ name: 'Home2' })
const { t } = useI18n()
const loading = ref(true)
const lineOptionsData = reactive<EChartsOption>(lineOptions) as EChartsOption
let data: any[] = [] // 波形图数据
// 初始数据
const getLineData = async () => {
// data = createData(1000000, 1)
set(
lineOptionsData,
'xAxis.data',
data.map((v) => t(v.name))
)
set(lineOptionsData, 'series', [
{
name: 'data1',
smooth: false, // 是否平滑曲线
type: 'line',
showSymbol: false, // 不显示数据点
progressive: 1000, // 每次增量渲染的数据量
progressiveThreshold: 5000, // 总数据量超过此值触发增量渲染
lineStyle: {
width: 1 // 细线
},
sampling: 'lttb', // 使用 LTTB 采样算法降低数据量
// samplingThreshold: 10000, // 采样阈值,超过此值将进行采样
animation: false, // 动画
animationDuration: 1000, // 动画持续时间
animationEasing: 'linear', // 动画缓动效果
data: data.map((v) => v.data1)
}
// {
// name: 'data2',
// smooth: false, // 是否平滑曲线
// type: 'line',
// showSymbol: false, // 不显示数据点
// progressive: 1000, // 每次增量渲染的数据量
// progressiveThreshold: 5000, // 总数据量超过此值触发增量渲染
// animation: false, // 关闭动画
// lineStyle: {
// width: 0.5 // 细线
// },
// sampling: 'lttb', // 使用 LTTB 采样算法
// samplingThreshold: 10000, // 采样阈值,超过此值将进行采样
// data: data.map((v) => v.data2)
// }
])
}
const connect = () => {
// 连接到 WebSocket 服务器
const socket = new WebSocket('ws://localhost:8080')
// 监听连接成功
socket.addEventListener('open', (event) => {
console.log('WebSocket 连接成功')
})
// 接收服务器消息
socket.addEventListener('message', (event) => {
const newData = JSON.parse(event.data)
// 更新图表数据
const newXAxisData = [...lineOptionsData!.xAxis!.data, ...newData.map((item) => item.name)]
console.log(newXAxisData.length)
set(lineOptionsData, 'xAxis.data', newXAxisData)
set(lineOptionsData, 'series', [
{
name: 'data1',
smooth: false,
type: 'line',
// 启用大数据优化
progressive: 1000,
progressiveThreshold: 5000,
showSymbol: false, // 不显示数据点
lineStyle: {
width: 0.5 // 细线
},
sampling: 'lttb', // 使用 LTTB 采样算法
animation: false, // 动画
animationDuration: 1000, // 动画持续时间
animationEasing: 'linear', // 动画缓动效果
data: [...lineOptionsData!.series![0].data, ...newData.map((item) => item.data1)]
}
// {
// name: 'data2',
// smooth: false,
// type: 'line',
// itemStyle: {},
// // 启用大数据优化
// progressive: 1000,
// progressiveThreshold: 5000,
// lineStyle: {
// width: 0.5 // 细线
// },
// data: [...lineOptionsData.series[1].data, ...newData.map((item) => item.data2)]
// }
])
})
}
const getAllApi = async () => {
await getLineData()
loading.value = false
connect()
}
getAllApi()
interface DataItem {
[key: string]: number | string
name: string
}
function formatDate(date: Date, format: string): string {
const pad = (num: number) => num.toString().padStart(2, '0')
return format
.replace('HH', pad(date.getHours()))
.replace('mm', pad(date.getMinutes()))
.replace('ss', pad(date.getSeconds()))
}
function createData(
count: number,
dataCount: 1 | 2 | 3 = 2,
minValue: number = 50,
maxValue: number = 250
): DataItem[] {
const result: DataItem[] = []
const now = new Date()
// 生成时间点(从当前时间往前递减)
const timeStamps: Date[] = []
for (let i = count - 1; i >= 0; i--) {
const time = new Date(now.getTime() - i * 1000)
timeStamps.push(time)
}
// 初始化各数据项的值和方向
const dataValues: Record<string, number> = {}
const dataDirections: Record<string, number> = {}
for (let i = 1; i <= dataCount; i++) {
const key = `data${i}`
dataValues[key] = minValue + Math.random() * (maxValue - minValue) // 初始随机值
dataDirections[key] = Math.random() > 0.5 ? 1 : -1 // 随机初始方向
}
for (let i = 0; i < count; i++) {
const item: DataItem = { name: formatDate(timeStamps[i], 'HH:mm:ss') }
// 为每个数据项生成波形数据
for (let j = 1; j <= dataCount; j++) {
const key = `data${j}`
const change = 10 + Math.random() * 30 // 变化幅度10~40(可调整)
// 随机决定是否改变方向(更自然的波动)
if (Math.random() < 0.2) {
// 20%概率改变方向
dataDirections[key] *= -1
}
dataValues[key] += dataDirections[key] * change
// 限制在合理范围内(但不强制反转)
dataValues[key] = Math.max(
minValue * 0.9, // 允许稍微低于最小值(避免卡边界)
Math.min(maxValue * 1.1, dataValues[key]) // 允许稍微高于最大值
)
item[key] = Math.round(dataValues[key])
}
result.push(item)
}
return result
}
</script>
<style lang="scss" scoped></style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment