前端可视化大屏实战:碳排放数据展示
效果预览
项目最终呈现为一个响应式的大屏界面,包含左侧、中间和右侧三个主要区域。中间核心位置展示 3D 地球模型及关键指标,两侧分布各类统计图表。整体采用深色科技风背景,支持动态切换不同场景(如教室、实验室、宿舍)的数据视图。
技术架构概览
本项目主要依赖以下技术栈:
- HTML/CSS: 负责页面骨架与样式布局,使用 Flexbox 实现三栏结构。
- JavaScript (ES6+): 核心交互逻辑,处理图表渲染与事件监听。
- ECharts: 用于绘制柱状图、折线图、饼图等常规统计图表。
- Three.js: 用于构建中间的 3D 地球动画效果。
- Jinja2: 模板引擎(从
url_for推断),用于资源路径管理。
核心代码解析
1. 页面结构与资源引入 (view.html)
页面主体分为 Header、Section 和 Footer 三部分。Header 显示标题与实时时间,Section 内部通过 .column 类划分左中右三列。值得注意的是,我们使用了 flexible.js 来适配不同分辨率的设备,确保在大屏上比例协调。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/view.css') }}">
</head>
<body>
<!-- 头部盒子 -->
<header>
<div class="container"><h1>碳排放可视化大屏</h1></div>
<div class="showTime"></div>
<script>
var t = null;
t = setTimeout(time, 1000);
function time() {
clearTimeout(t);
var a = new Date();
var y = a.getFullYear();
var mt = a.getMonth() + 1;
var day = a.getDate();
var h = a.getHours();
var m = a.getMinutes();
var s = a.getSeconds();
document.querySelector(".showTime").innerHTML = '当前时间:' + y + "年" + mt + "月" + day + "-" + h + "时" + m + "分" + s + "秒";
t = setTimeout(time, 1000);
}
</script>
</header>
<!-- 页面主体部分 -->
<section class="box">
<!-- 左侧盒子 -->
<div class="column">
<!-- 左侧柱状图模块 -->
<div class="public bar right-chart-module">
<div class="chart-title">
<h2></h2>
<div class="chart-tabs">
<button class="tab-btn active" data-type="classroom">教室</button>
<button class="tab-btn" data-type="lab">实验室</button>
<button class="tab-btn" data-type="dormitory">宿舍</button>
</div>
</div>
<div class="char" id="leftBarChart"></div>
<div class="public-one"></div>
</div>
<!-- 其他模块... -->
</div>
<!-- 中间盒子 -->
<div class="column">
<!-- num 模块 -->
<div class="num">
<div class="globe-container">
<canvas id="globe-canvas"></canvas>
</div>
<!-- 数据展示... -->
</div>
</div>
<!-- 右侧盒子 -->
<div class="column">
<!-- 右侧柱状图模块 -->
<div class="public bar right-chart-module">
<div class="chart-title">
<h2></h2>
<div class="chart-tabs">
<button class="tab-btn active" data-type="classroom">教室</button>
<button class="tab-btn" data-type="lab">实验室</button>
<button class="tab-btn" data-type="dormitory">宿舍</button>
</div>
</div>
<div class="char" id="vehicleChart"></div>
<div class="public-one"></div>
</div>
<!-- 饼图模块 -->
<div class="public pie">
<div class="chart-title">
<h2></h2>
<div class="chart-tabs">
<button class="tab-btn active" data-time="morning">早</button>
<button class="tab-btn" data-time="evening">晚</button>
</div>
</div>
<div class="char" id="pieChart"></div>
<div class="public-one"></div>
</div>
</div>
</section>
<script src="{{ url_for('static', filename='js/echarts.js') }}"></script>
<script src="{{ url_for('static', filename='js/echarts.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/view.js') }}"></script>
<script src="{{ url_for('static', filename='js/flexible.js') }}"></script>
<script src="{{ url_for('static', filename='js/jiazai.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => window.dispatchEvent(new Event('resize')), 300);
});
</script>
</body>
</html>
2. 样式适配与布局 (view.css)
样式部分采用了 Rem 单位配合 flexible.js 进行自适应。背景图设置为固定定位,避免滚动时闪烁。图表容器使用了半透明边框和四角装饰,增强科技感。特别需要注意的是媒体查询部分,针对不同屏幕宽度调整了字体大小,保证移动端也能查看。
* { margin: 0; padding: 0; box-sizing: border-box; }
li { list-style: none; }
@font-face { font-family: electronicFont; src: url(/static/font/ziti.TTF); }
html { font-size: 100px; overflow: hidden; width: 100%; height: 100%; }
body {
font-family: Arial, Helvetica, sans-serif;
background: url(/static/images/bei.jpg) no-repeat;
background-size: cover;
background-attachment: fixed;
line-height: 1.15;
width: 100%;
height: 100%;
overflow: hidden;
}
header {
position: relative;
height: 1.11rem;
background: url(/static/images/head.png) no-repeat top center;
background-size: 100% 100%;
width: 100%;
}
header .container h1 {
font-size: 0.51rem;
color: #fff;
text-align: center;
line-height: 1.07rem;
-webkit-text-stroke: 2px #00f7ff;
text-shadow: 0 0 10px rgba(0, 247, 255, 0.5);
}
.box { display: flex; min-width: 1024px; max-width: 1920px; width: 100%; height: calc(100% - 1.11rem); margin: 0 auto; padding: 0.13rem; overflow: hidden; }
.box .column { flex: 3; height: 100%; display: flex; flex-direction: column; }
.box .column:nth-child(2) { flex: 5; margin: 0 0.13rem; }
/* ... 更多样式省略 ... */
@media screen and (max-width: 1024px) { html { font-size: 42px !important; } }
@media screen and (min-width: 1920px) { html { font-size: 80px !important; } }
3. 图表交互逻辑 (view.js)
JS 文件是核心所在,我们将不同图表封装在 IIFE 中以避免污染全局变量。每个图表模块都配置了独立的数据源,并通过 Tab 按钮触发 updateChart 函数来切换数据。
以柱状图为例,我们定义了 chartData 对象存储不同场景(教室、实验室、宿舍)的用电量数据。当用户点击 Tab 时,移除旧激活状态,更新当前类型并重新渲染 ECharts 实例。同时监听了 window.resize 事件,确保窗口缩放时图表自动适应。
// 柱状图模块
(function(){
const chartData = {
classroom: { title: '教室每日用电量统计', data: { categories: ['周一','周二','周三','周四','周五','周六','周日'], series: [{ name:'早', data:[6.28,9.55,8.69,8.63,6.64,4.17,2.69] }, { name:'晚', data:[5.71,8.65,5.32,5.91,5.77,3.17,1.85]} ] }, colors:['#5470C6','#91CC75'] },
lab: { title: '实验室每日用电量统计', data: { categories: ['周一','周二','周三','周四','周五','周六','周日'], series: [{ name:'早', data:[13.85,17.78,20.12,16.77,14.69,3.46,5.72] }, { name:'晚', data:[9.05,11.72,14.37,11.42,11.62,3.17,5.63]} ] }, colors:['#EE6666','#FAC858'] },
dormitory: { title: '宿舍每日用电量统计', data: { categories: ['周一','周二','周三','周四','周五','周六','周日'], series: [{ name:'早', data:[4.23,3.98,4.56,4.01,4.34,2.78,2.45] }, { name:'晚', data:[5.31,5.45,5.12,5.78,6.89,7.01,5.67]} ] }, colors:['#73C0DE','#3BA272'] }
};
document.addEventListener('DOMContentLoaded', function(){
var barChartDom = document.querySelector('.bar .char');
var titleElement = document.querySelector('.bar .chart-title h2');
var tabButtons = document.querySelectorAll('.bar .tab-btn');
if(barChartDom && titleElement && tabButtons.length >0){
var barChart = echarts.init(barChartDom);
var currentType = 'classroom';
function updateChart(type){
const data = chartData[type].data;
const colors = chartData[type].colors;
const barOption = {
color: colors,
title: { text: chartData[type].title, left:'center', textStyle:{fontSize:16,color:'#fff'}, top:-3 },
tooltip: { trigger:'axis', axisPointer:{type:'shadow'} },
legend: { data:['早','晚'], textStyle:{color:'#fff'}, top:20 },
grid: { top:'20%', left:'3%', right:'4%', bottom:'3%', containLabel:true },
xAxis: { type:'category', data: data.categories, axisLine:{lineStyle:{color:'#fff'}}, axisLabel:{color:'#fff',interval:0} },
yAxis: { type:'value', name:'用电量 (千瓦)', nameTextStyle:{color:'#fff',padding:[0,0,0,30]}, axisLine:{show:true,lineStyle:{color:'#fff'}}, axisLabel:{color:'#fff'}, splitLine:{lineStyle:{color:'rgba(255, 255, 255, 0.2)'}} },
series: [
{name:'早',type:'bar',barWidth:'30%',barGap:'50%',data: data.series[0].data,},
{name:'晚',type:'bar',barWidth:'30%',data: data.series[1].data,}
]
};
barChart.setOption(barOption);
}
updateChart(currentType);
tabButtons.forEach(button=>{
button.addEventListener('click',function(){
tabButtons.forEach(btn=> btn.classList.remove('active'));
this.classList.add('active');
currentType = this.dataset.type;
updateChart(currentType);
});
});
window.addEventListener('resize',function(){ barChart.resize(); });
}
});
})();
4. 3D 地球动画 (initGlobe)
中间区域的地球使用了 Three.js 构建。我们通过加载纹理贴图(法线贴图、高光贴图)来增加真实感。光照系统包含环境光和方向光,模拟自然光照效果。动画循环中不断旋转地球 Mesh,营造动态视觉体验。
// 初始化 3D 地球
function initGlobe(){
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('globe-canvas'), antialias: true, alpha: true });
renderer.setSize(400, 300);
renderer.setClearColor(0x000000, 0);
const geometry = new THREE.SphereGeometry(5, 64, 64);
const material = new THREE.MeshPhongMaterial({
color: 0x156289, emissive: 0x072534, shininess: 5, transparent: true, opacity: 0.8, wireframe: false
});
const textureLoader = new THREE.TextureLoader();
material.map = textureLoader.load('/static/images/img.png');
material.bumpMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_normal_2048.jpg');
material.specularMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_specular_2048.jpg');
const earth = new THREE.Mesh(geometry, material);
scene.add(earth);
const ambientLight = new THREE.AmbientLight(0x333333);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 3, 5);
scene.add(directionalLight);
camera.position.z = 12;
function animate(){
requestAnimationFrame(animate);
earth.rotation.y += 0.002;
renderer.render(scene, camera);
}
animate();
function resizeGlobe(){
const container = document.querySelector('.globe-container');
const width = container.clientWidth;
const height = container.clientHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
window.addEventListener('resize', resizeGlobe);
resizeGlobe();
}
if(typeof THREE === 'undefined'){
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js';
script.onload = initGlobe;
document.head.appendChild(script);
}else{
initGlobe();
}
5. 辅助工具
项目中还包含了 jiazai.js 用于处理资源加载缓存问题,以及 flexible.js 用于计算 Rem 基准值,确保在不同设备上的布局一致性。这些工具函数虽然不起眼,但对大屏项目的稳定性至关重要。


