一、项目概述与技术选型
数据可视化大屏是企业数据展示的重要形式,本教程将使用纯JavaScript技术栈实现一个完整的实时数据可视化大屏系统。
核心技术栈:
- 图表库:ECharts 5.x
- 实时通信:WebSocket
- UI框架:无依赖纯CSS布局
- 数据处理:JavaScript ES6+
实现功能:
- 实时销售数据仪表盘
- 地理分布热力图
- 动态排名柱状图
- 实时数据推送与更新
- 自适应多种屏幕尺寸
二、项目结构与初始化
1. 基础HTML结构
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>企业数据可视化大屏</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/map/js/china.js"></script>
</head>
<body>
<div class="dashboard-container">
<header class="dashboard-header">
<h1>企业运营实时监控系统</h1>
<div class="real-time-info">
<span id="current-time"></span>
<span id="data-update-time">最后更新: --</span>
</div>
</header>
<div class="dashboard-content">
<div class="panel sales-panel" id="sales-chart"></div>
<div class="panel map-panel" id="map-chart"></div>
<div class="panel ranking-panel" id="ranking-chart"></div>
<div class="panel kpi-panel" id="kpi-container"></div>
</div>
</div>
<script src="js/app.js"></script>
</body>
</html>
2. 基础CSS布局
/* 使用内联样式代替style标签 */
.dashboard-container {
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #0f1c3c;
color: #fff;
font-family: "Microsoft YaHei", sans-serif;
}
.dashboard-header {
height: 80px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30px;
background: linear-gradient(to right, #1a3a8f, #0f1c3c);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
.dashboard-content {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 20px;
padding: 20px;
height: calc(100vh - 80px);
}
.panel {
background: rgba(16, 31, 63, 0.8);
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
position: relative;
}
.sales-panel { grid-area: 1 / 1 / 2 / 2; }
.map-panel { grid-area: 1 / 2 / 3 / 3; }
.ranking-panel { grid-area: 2 / 1 / 3 / 2; }
.kpi-panel { grid-area: 3 / 1 / 4 / 3; display: none; }
三、核心JavaScript实现
1. 初始化所有图表
// app.js
document.addEventListener('DOMContentLoaded', function() {
// 初始化所有图表
const salesChart = initSalesChart();
const mapChart = initMapChart();
const rankingChart = initRankingChart();
// 更新时间显示
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 初始化WebSocket连接
initWebSocket(salesChart, mapChart, rankingChart);
});
function updateCurrentTime() {
const now = new Date();
document.getElementById('current-time').textContent =
`当前时间: ${now.toLocaleString('zh-CN', { hour12: false })}`;
}
2. 销售数据仪表盘实现
function initSalesChart() {
const chartDom = document.getElementById('sales-chart');
const myChart = echarts.init(chartDom);
const option = {
title: {
text: '实时销售数据',
left: 'center',
textStyle: { color: '#fff' }
},
tooltip: { trigger: 'axis' },
legend: {
data: ['线上销售', '线下销售', '总销售额'],
textStyle: { color: '#ccc' },
top: 30
},
grid: { top: 80, right: 30, bottom: 30, left: 60 },
xAxis: {
type: 'category',
data: [],
axisLine: { lineStyle: { color: '#4a5e8c' } },
axisLabel: { color: '#8b9dc7' }
},
yAxis: {
type: 'value',
axisLine: { show: true, lineStyle: { color: '#4a5e8c' } },
axisLabel: { color: '#8b9dc7' },
splitLine: { lineStyle: { color: 'rgba(74, 94, 140, 0.3)' } }
},
series: [
{
name: '线上销售',
type: 'line',
smooth: true,
data: [],
lineStyle: { width: 3, color: '#4cc9f0' },
itemStyle: { color: '#4cc9f0' }
},
{
name: '线下销售',
type: 'line',
smooth: true,
data: [],
lineStyle: { width: 3, color: '#f72585' },
itemStyle: { color: '#f72585' }
},
{
name: '总销售额',
type: 'bar',
barWidth: '60%',
data: [],
itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#4899f1' },
{ offset: 1, color: '#1a3a8f' }
]) }
}
]
};
myChart.setOption(option);
window.addEventListener('resize', function() { myChart.resize(); });
return myChart;
}
3. 地理分布热力图实现
function initMapChart() {
const chartDom = document.getElementById('map-chart');
const myChart = echarts.init(chartDom);
// 注册地图
echarts.registerMap('china', china);
const option = {
title: {
text: '销售区域分布',
left: 'center',
textStyle: { color: '#fff' }
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} (万元)'
},
visualMap: {
min: 0,
max: 1000,
text: ['高', '低'],
realtime: false,
calculable: true,
inRange: {
color: ['#1a3a8f', '#4cc9f0', '#f72585']
},
textStyle: { color: '#fff' }
},
series: [{
name: '销售额',
type: 'map',
map: 'china',
roam: true,
emphasis: {
label: { show: true }
},
data: []
}]
};
myChart.setOption(option);
window.addEventListener('resize', function() { myChart.resize(); });
return myChart;
}
4. 动态排名柱状图实现
function initRankingChart() {
const chartDom = document.getElementById('ranking-chart');
const myChart = echarts.init(chartDom);
const option = {
title: {
text: '产品销量排名',
left: 'center',
textStyle: { color: '#fff' }
},
grid: { top: 40, right: 10, bottom: 20, left: 120 },
xAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#4a5e8c' } },
axisLabel: { color: '#8b9dc7' },
splitLine: { lineStyle: { color: 'rgba(74, 94, 140, 0.3)' } }
},
yAxis: {
type: 'category',
data: [],
axisLine: { show: true, lineStyle: { color: '#4a5e8c' } },
axisLabel: { color: '#8b9dc7' }
},
series: [{
name: '销量',
type: 'bar',
data: [],
label: {
show: true,
position: 'right',
color: '#fff'
},
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#f72585' },
{ offset: 1, color: '#7209b7' }
])
}
}]
};
myChart.setOption(option);
window.addEventListener('resize', function() { myChart.resize(); });
return myChart;
}
四、实时数据通信实现
1. WebSocket连接管理
function initWebSocket(salesChart, mapChart, rankingChart) {
// 模拟WebSocket连接,实际项目中替换为真实WebSocket连接
const ws = {
onmessage: null,
send: function(data) {
console.log('Send:', data);
},
close: function() {
console.log('WebSocket closed');
}
};
// 模拟接收消息
setInterval(() => {
if (ws.onmessage) {
const mockData = generateMockData();
ws.onmessage({ data: JSON.stringify(mockData) });
}
}, 3000);
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
updateDataUpdateTime();
updateAllCharts(data, salesChart, mapChart, rankingChart);
};
// 窗口关闭时断开连接
window.addEventListener('beforeunload', function() {
ws.close();
});
return ws;
}
function updateDataUpdateTime() {
const now = new Date();
document.getElementById('data-update-time').textContent =
`最后更新: ${now.toLocaleTimeString('zh-CN', { hour12: false })}`;
}
2. 模拟数据生成
function generateMockData() {
// 生成模拟数据
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
// 销售数据
const salesData = {
categories: [],
online: [],
offline: [],
total: []
};
for (let i = 0; i ({
name: province,
value: Math.round(Math.random() * 900 + 100)
}));
// 排名数据
const products = [
'智能手机', '笔记本电脑', '平板电脑', '智能手表',
'无线耳机', '智能电视', '游戏主机', '数码相机'
];
const rankingData = products.map(product => ({
name: product,
value: Math.round(Math.random() * 1000 + 500)
})).sort((a, b) => b.value - a.value);
return {
sales: salesData,
map: mapData,
ranking: rankingData
};
}
3. 图表数据更新
function updateAllCharts(data, salesChart, mapChart, rankingChart) {
// 更新销售图表
salesChart.setOption({
xAxis: { data: data.sales.categories },
series: [
{ data: data.sales.online },
{ data: data.sales.offline },
{ data: data.sales.total }
]
});
// 更新地图
mapChart.setOption({
series: [{ data: data.map }]
});
// 更新排名图表
rankingChart.setOption({
yAxis: { data: data.ranking.map(item => item.name) },
series: [{ data: data.ranking.map(item => item.value) }]
});
}
五、性能优化与高级技巧
1. 图表性能优化
- 数据采样:对于高频数据,进行适当采样减少渲染压力
- 动画优化:适当关闭不必要的动画效果
- 防抖处理:对窗口resize事件进行防抖处理
2. WebSocket最佳实践
// 实际项目中的WebSocket实现示例
function createRealWebSocket(url, charts) {
const socket = new WebSocket(url);
socket.onopen = function() {
console.log('WebSocket连接已建立');
// 发送初始请求
socket.send(JSON.stringify({ type: 'init' }));
};
socket.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
updateDataUpdateTime();
updateAllCharts(data, ...charts);
} catch (e) {
console.error('数据解析错误:', e);
}
};
socket.onclose = function() {
console.log('WebSocket连接关闭');
// 尝试重新连接
setTimeout(() => createRealWebSocket(url, charts), 5000);
};
socket.onerror = function(error) {
console.error('WebSocket错误:', error);
};
return socket;
}
3. 内存管理
长时间运行的大屏应用需要注意内存管理:
- 定期清理不再使用的数据
- 避免内存泄漏(如未移除的事件监听器)
- 对于隐藏的图表,可以调用dispose()释放资源
六、项目部署与监控
1. 生产环境部署
建议的部署方案:
- 使用Nginx作为静态资源服务器
- 启用Gzip压缩减少传输体积
- 配置适当的缓存策略
- 使用CDN加速资源加载
2. 监控与异常处理
// 全局错误监控
window.addEventListener('error', function(event) {
// 可以将错误信息发送到监控系统
console.error('全局捕获的错误:', event.error);
});
// 图表渲染错误处理
function initChartWithErrorHandling(domId) {
try {
const chart = echarts.init(document.getElementById(domId));
chart.on('rendered', function() {
console.log(`${domId} 渲染完成`);
});
return chart;
} catch (error) {
console.error(`初始化图表 ${domId} 失败:`, error);
// 显示友好的错误提示
document.getElementById(domId).innerHTML =
'<div class="chart-error">图表加载失败,请刷新重试</div>';
return null;
}
}
七、总结与扩展
本教程完整演示了如何使用纯JavaScript技术栈开发实时数据可视化大屏:
- 使用ECharts实现多种图表类型
- 通过WebSocket实现实时数据更新
- 设计了响应式布局适应不同屏幕
- 实现了完整的数据模拟和更新逻辑
扩展方向:
- 接入真实后端API数据
- 增加更多图表类型(如关系图、3D图表)
- 实现用户交互功能(如数据筛选、时间范围选择)
- 增加主题切换功能
完整项目代码已上传GitHub:https://github.com/example/js-data-dashboard