跳到主要内容
基于 ECharts 与 Three.js 的碳排放可视化大屏实现 | 极客日志
JavaScript 大前端
基于 ECharts 与 Three.js 的碳排放可视化大屏实现 本项目展示了一个基于 Web 技术的碳排放可视化大屏解决方案。核心采用 ECharts 进行多维度数据图表渲染,结合 Three.js 实现 3D 地球效果。页面布局使用 Flexbox 配合 rem 单位适配不同分辨率,涵盖教室、实验室及宿舍的用电用水统计。代码结构清晰,包含主界面模板、样式定义及交互逻辑,适合用于智慧校园或能源管理系统的参考实现。
一、效果预览
二、技术架构
本项目采用原生 HTML/CSS/JavaScript 构建,核心依赖 ECharts 处理二维数据图表,结合 Three.js 渲染三维地球效果。页面布局使用 Flexbox 配合 rem 单位适配不同分辨率,涵盖教室、实验室及宿舍的用电用水统计。
三、核心代码解析
1. 页面结构 (view.html)
主界面通过语义化标签划分头部、主体和侧边栏。头部展示标题与实时时间,主体分为左中右三列,分别承载柱状图、3D 地球数据和饼图。
<!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" >
教室
实验室
宿舍
无纸化考试次数
12
没想好
419
没想好
10.5
没想好
4.7
没想好
教室
实验室
宿舍
早
晚
</div >
<script >
var t = null ;
t = setTimeout (time, 1000 );
function time ( ) {
clearTimeout (t);
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 class ="public bar" >
<h2 >
</h2 >
<div class ="chart-tabs" >
<div class ="char" id ="horizontalBarChart" >
</div >
</div >
<div class ="public-one" >
</div >
</div >
</div >
<div class ="column" >
<div class ="num" >
<div class ="globe-container" >
<canvas id ="globe-canvas" >
</canvas >
</div >
<div class ="globe-data" >
<div class ="data-row" >
<div class ="data-item" >
<div class ="data-value" id ="global-temp" >
</div >
<div class ="data-label" >
</div >
</div >
<div class ="data-item" >
<div class ="data-value" id ="co2-level" >
</div >
<div class ="data-label" >
</div >
</div >
</div >
<div class ="data-row" >
<div class ="data-item" >
<div class ="data-value" id ="china-emission" >
</div >
<div class ="data-label" >
</div >
</div >
<div class ="data-item" >
<div class ="data-value" id ="world-emission" >
</div >
<div class ="data-label" >
</div >
</div >
</div >
</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 单位配合媒体查询来保证在不同屏幕下的显示效果。
* { 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; margin : 0 ; padding : 0 ; 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 ); }
header .showTime { position : absolute; right : 0.4rem ; top : 0 ; line-height : 1rem ; color : #2680ce ; font-size : 0.27rem ; font-family : 'electronicFont' ; white-space : nowrap; }
.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 ; }
.box .public { position : relative; height : 5.6rem ; border : 1px solid rgba (25 , 186 , 139 , 0.17 ); background : url (/static/images/bian.png ) rgba (255 , 255 , 255 , 0.04 ); padding : 0 0.2rem 0.53rem ; margin-bottom : 0.2rem ; width : 100% ; }
@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 部分负责图表初始化、数据更新及 3D 场景渲染。ECharts 配置项较为复杂,建议将通用配置提取为函数。Three.js 部分用于加载地球模型并添加光照效果。
(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' ]
},
};
document .addEventListener ('DOMContentLoaded' , function ( ) {
var barChartDom = document .querySelector ('.bar .char' );
if (barChartDom) {
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' } },
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);
var tabButtons = document .querySelectorAll ('.bar .tab-btn' );
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 (); });
}
});
})();
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 ();
}
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 ();
}
四、关键细节
响应式适配 :通过 flexible.js 动态计算 rem 基准,确保在不同分辨率下布局比例一致。
图表联动 :Tab 切换时移除所有按钮的 active 类,仅高亮当前选中项,并触发对应数据更新。
性能优化 :Canvas 渲染减少 DOM 操作,动画循环使用 requestAnimationFrame 保证流畅度。
以上即为整个大屏的核心实现思路,实际开发中可根据业务需求调整数据源与配色方案。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online