用ECharts地图做个疫情数据看板:从静态打点到动态飞线,Vue3+TS实战教程
·
Vue3+TS与ECharts实战:构建动态疫情数据可视化看板
1. 项目背景与技术选型
在数据驱动的时代,可视化看板已成为企业决策和公共信息展示的重要工具。最近接到一个需求:为某机构开发一个能够实时展示疫情动态的可视化看板,需要包含地图打点、数据趋势和人员流动模拟等功能。
经过技术评估,我们选择了以下技术栈组合:
- Vue3 :提供响应式数据和组件化开发体验
- TypeScript :增强代码可维护性和开发效率
- ECharts :强大的可视化库,特别适合地理数据展示
- Vite :极速的开发构建工具
# 创建项目
npm create vite@latest pandemic-dashboard --template vue-ts
这个组合不仅能满足当前需求,还能为未来功能扩展打下良好基础。特别是ECharts的地图功能,它支持:
- 中国地图及各省市精细展示
- 动态数据更新
- 丰富的交互效果
- 高性能渲染
2. 基础地图搭建
2.1 安装依赖与基础配置
首先需要安装必要的依赖:
npm install echarts vue-echarts
npm install @types/echarts -D
然后创建一个基础地图组件 BaseMap.vue :
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'
import chinaJson from './china.json' // 中国地理JSON数据
const chartRef = ref<HTMLElement>()
onMounted(() => {
const chart = echarts.init(chartRef.value!)
echarts.registerMap('china', chinaJson)
const option: echarts.EChartsOption = {
geo: {
map: 'china',
roam: true,
label: {
show: true,
color: '#333'
},
itemStyle: {
areaColor: '#e7f8ff',
borderColor: '#2980b9'
}
}
}
chart.setOption(option)
})
</script>
<template>
<div ref="chartRef" class="h-[600px] w-full"></div>
</template>
2.2 地图数据优化
为了提升性能,我们需要对地图数据进行一些优化处理:
- 简化地理数据 :使用简化版的GeoJSON减少体积
- 按需加载 :只在需要时加载详细区域数据
- 缓存机制 :对已加载的地图数据进行缓存
// 在组件中添加这段代码
const loadProvinceDetail = async (provinceName: string) => {
const res = await import(`./provinces/${provinceName}.json`)
echarts.registerMap(provinceName, res.default)
}
3. 疫情数据可视化
3.1 静态数据打点展示
疫情数据通常包含以下维度:
- 确诊人数
- 治愈人数
- 死亡人数
- 高风险区域
我们可以用不同视觉元素表示这些数据:
| 数据类型 | 视觉元素 | 颜色编码 |
|---|---|---|
| 确诊人数 | 圆形大小 | 红色系 |
| 治愈人数 | 环形图 | 绿色系 |
| 死亡人数 | 三角形 | 灰色系 |
| 高风险区 | 闪烁效果 | 深红色 |
实现代码示例:
const renderCaseData = (chart: echarts.ECharts, data: CaseData[]) => {
const option: echarts.EChartsOption = {
series: [
{
type: 'scatter',
coordinateSystem: 'geo',
data: data.map(item => ({
name: item.area,
value: [...item.coords, item.confirmed],
symbolSize: Math.min(30, Math.max(5, item.confirmed / 1000)),
itemStyle: {
color: getColorBySeverity(item.confirmed)
}
})),
encode: {
value: 2
}
}
]
}
chart.setOption(option)
}
function getColorBySeverity(count: number): string {
if (count > 1000) return '#ff0000'
if (count > 500) return '#ff4500'
if (count > 100) return '#ff8c00'
return '#ffd700'
}
3.2 动态飞线效果
人员流动是疫情传播的重要途径,用飞线动画可以直观展示这一过程:
const renderMigrationLines = (chart: echarts.ECharts, routes: MigrationRoute[]) => {
const option: echarts.EChartsOption = {
series: [
{
type: 'lines',
coordinateSystem: 'geo',
data: routes.map(route => ({
coords: [route.from, route.to],
lineStyle: {
width: Math.min(5, route.count / 1000),
curveness: 0.2
},
effect: {
show: true,
period: 4,
trailLength: 0.02,
symbol: 'arrow',
symbolSize: 8
}
})),
progressiveThreshold: 1000,
progressive: 200
}
]
}
chart.setOption(option)
}
提示:飞线数量较多时会影响性能,建议添加阈值过滤,只显示主要流动路线
4. 高级功能实现
4.1 实时数据更新
疫情数据需要实时更新,我们可以使用WebSocket或定时轮询:
// 在组件setup函数中添加
const setupDataUpdates = () => {
const socket = new WebSocket('wss://api.example.com/ws')
socket.onmessage = (event) => {
const data = JSON.parse(event.data)
updateChart(data)
}
// 备用方案:定时请求
const fallbackInterval = setInterval(async () => {
const res = await fetch('/api/latest-data')
const data = await res.json()
updateChart(data)
}, 30000)
onUnmounted(() => {
socket.close()
clearInterval(fallbackInterval)
})
}
const updateChart = (data: UpdateData) => {
// 使用增量更新优化性能
chart.setOption({
series: [{
data: processUpdateData(data)
}]
}, true) // 注意这里的true表示不合并旧配置
}
4.2 性能优化技巧
随着数据量增加,性能可能成为瓶颈。以下是一些优化建议:
- 数据聚合 :对小区域数据进行聚类展示
- 渲染节流 :限制高频更新的渲染频率
- 离屏Canvas :对复杂图表使用离屏渲染
- Web Worker :将数据处理移出主线程
// 使用防抖处理频繁更新
const debouncedUpdate = useDebounceFn(updateChart, 300)
// 在Web Worker中处理数据
const worker = new Worker('./data-processor.js')
worker.onmessage = (e) => {
chart.setOption(e.data)
}
4.3 交互增强
良好的交互可以提升用户体验:
- 区域钻取 :点击省份查看详情
- 数据筛选 :按时间、类型等维度过滤
- 图例控制 :显示/隐藏特定数据系列
- 提示框定制 :显示更丰富的信息
实现区域钻取的代码示例:
chart.on('click', 'series', (params) => {
if (params.componentType === 'series' && params.seriesType === 'scatter') {
// 显示该地区详细信息
showAreaDetail(params.name)
}
})
const showAreaDetail = (areaName: string) => {
// 加载该地区详细数据
const detailOption: echarts.EChartsOption = {
geo: {
map: areaName,
// 其他配置...
}
}
chart.setOption(detailOption)
}
5. 项目工程化实践
5.1 组件化架构
将图表封装为可复用的组件:
components/
├── PandemicMap.vue # 主地图组件
├── DataLegend.vue # 图例控制
├── TimeControls.vue # 时间选择器
└── utils/
├── chart-options.ts # 图表配置生成器
├── data-loader.ts # 数据加载逻辑
└── types.ts # 类型定义
5.2 类型安全设计
使用TypeScript增强代码可靠性:
// types.ts
interface CaseData {
area: string
coords: [number, number]
confirmed: number
cured: number
deaths: number
timestamp: number
}
interface MigrationRoute {
from: [number, number]
to: [number, number]
count: number
date: string
}
type SeverityLevel = 'low' | 'medium' | 'high' | 'critical'
5.3 样式与主题定制
ECharts支持灵活的主题配置:
// theme/dark.js
export default {
backgroundColor: '#1e1e1e',
geo: {
itemStyle: {
areaColor: '#2a2d3d',
borderColor: '#3c3f51'
}
},
textStyle: {
color: '#e0e0e0'
}
}
然后在项目中注册主题:
import darkTheme from './theme/dark'
echarts.registerTheme('dark', darkTheme)
const chart = echarts.init(dom, 'dark')
6. 部署与监控
6.1 构建优化
配置vite构建选项:
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
echarts: ['echarts']
}
}
}
}
})
6.2 错误监控
添加错误边界和监控:
// 错误边界组件
<script setup lang="ts">
const props = defineProps<{
fallback?: string
}>()
const hasError = ref(false)
onErrorCaptured((err) => {
hasError.value = true
logError(err)
return false
})
function logError(error: Error) {
// 发送到监控系统
fetch('/api/error-log', {
method: 'POST',
body: JSON.stringify({
error: error.stack,
component: 'PandemicMap'
})
})
}
</script>
<template>
<slot v-if="!hasError" />
<div v-else class="error-fallback">
{{ fallback || '图表加载失败' }}
</div>
</template>
6.3 性能监控
使用浏览器API监控性能:
const startTime = performance.now()
// 图表初始化后
chart.on('rendered', () => {
const loadTime = performance.now() - startTime
if (loadTime > 1000) {
console.warn(`图表加载耗时 ${loadTime}ms,考虑优化`)
}
})
更多推荐

所有评论(0)