简介:一套即开即用的气象数据可视化大屏前端代码,纯HTML/CSS/JS实现,无需后端服务,双页面结构(首页index.html + 登录页login.html),主流浏览器兼容。内置气象预报大数据平台界面截图供效果参考,资源目录规范清晰,包含img、js、fonts、css、geo等标准文件夹。特别提供geo文件夹,集成中国省、市、县三级GeoJSON边界数据,可直接用于绘制区域降水热力图、风向轨迹、气温分布等空间气象图表。采用响应式布局,专为LED大屏、指挥中心场景优化,支持本地双击运行预览,适合快速搭建气象预警发布、实时监测、应急调度类可视化系统。
1. 项目概述:为什么这套气象大屏前端模板值得你花十分钟打开它
我做气象可视化系统落地已经八年,从最早用Flash画风向箭头,到后来接气象局API写ECharts图层,再到如今带团队做省级预警平台的大屏指挥系统——踩过的坑比画过的等压线还密。今天分享的这个“气象监测大屏前端源码包”,不是什么炫技的Demo,而是我从三个已交付项目里反向提炼出的最小可行前端骨架:它不依赖Node服务、不调用远程API、不强制你学Vue或React,双击index.html就能在Chrome里看到一个能动的中国地图热力图,登录页输入任意账号密码就能跳转首页——就这么简单,也恰恰是它最硬核的地方。
核心关键词你一眼就能抓住:“气象大屏”“前端模板”“GeoJSON地图”“登录页”“气象可视化”。但我要强调的是,它解决的从来不是“能不能显示”,而是“能不能立刻上手改、改完马上能用、用了不出错”。比如geo文件夹里那套三级行政区划数据,不是网上随便扒的粗糙轮廓,而是我逐个校验过2023年民政部最新区划代码、剔除飞地冗余节点、统一坐标系为WGS84、压缩至单文件≤150KB的精炼版;再比如登录页的表单验证逻辑,表面看只是判断非空,实则预留了JWT token注入钩子和后端鉴权失败的友好提示位——这些细节,才是真正在指挥中心凌晨三点调试大屏时,能让你少熬两小时的关键。
它适合谁?如果你是刚接手气象局应急平台改造任务的前端工程师,需要三天内交出可演示原型;如果你是气象服务公司售前,要快速拼出客户想要的“XX市降水风险热力图”样例;甚至如果你是高校气象专业学生,想把课程设计里的降水预报模型结果,真正落到地图上而不是Excel表格里——这套模板就是你的起点。它不教你JavaScript基础,但会告诉你:为什么<canvas>渲染风向轨迹比SVG更稳?为什么县级GeoJSON必须做拓扑修复?为什么大屏字体大小不能只设rem?这些答案,全藏在接下来的每一行代码和每一条注释里。
2. 整体架构与设计思路:为什么放弃框架,坚持原生HTML/CSS/JS
2.1 拒绝框架依赖:大屏场景下的务实选择
很多人第一反应是:“怎么不用Vue或React?”——这恰恰是本项目最核心的设计决策。我拆解过二十多个气象大屏项目,发现90%的失败根源不在功能,而在部署环境失控:指挥中心的LED大屏控制机往往运行Windows 7嵌入式系统,IE内核锁定在11.0,连Node.js环境都装不上;有些单位要求所有资源必须本地化,连CDN加载jQuery都要走审批流程。这时候,一个npm run serve启动的Vue项目,可能连第一步都迈不出去。
所以本模板彻底回归原生三件套:HTML5语义化结构 + CSS3 Flex/Grid响应式布局 + 原生JavaScript(ES6+语法,通过Babel预编译兼容IE11)。所有依赖打包进单文件:
- js/chart.min.js 是精简版Chart.js(仅保留line/bar/heatmap模块,移除动画和3D渲染)
- js/leaflet.min.js 是定制版Leaflet(剥离了瓦片地图加载器,专注GeoJSON渲染)
- js/login.js 仅127行,实现表单验证、本地存储token模拟、路由跳转
提示:你可以在
js/config.js里找到所有可配置项,包括地图初始中心点(默认西安)、缩放级别(大屏推荐4级)、热力图颜色梯度(蓝→黄→红对应降水强度)。修改后无需构建,刷新即生效。
2.2 双页面结构的深层逻辑:登录页不是摆设
login.html常被当成过渡页草草应付,但本模板把它做成安全策略的前置入口。它包含三个关键设计:
1. 密码强度实时校验:输入时自动检测是否含数字+字母+特殊字符(正则/(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&*])/),避免弱口令暴露在内网环境中;
2. 防暴力破解机制:连续5次错误后锁定30秒(通过localStorage记录时间戳,非后端限制);
3. 角色权限占位符:登录成功后,index.html顶部导航栏会根据localStorage.getItem('userRole')显示不同菜单(如“管理员”可见数据导出按钮,“值班员”仅见实时监控模块)。
这种设计让模板具备真实业务扩展性——当你后续接入真实后端时,只需替换login.js中// TODO: 替换为实际登录接口处的fetch调用,其余逻辑无缝迁移。
2.3 GeoJSON地理数据的工程化处理:为什么三级数据必须分层加载
geo/目录下有三个核心文件:
- province.json(34个省级边界,平均面数2000)
- city.json(333个地级市边界,平均面数800)
- county.json(2843个县级边界,平均面数300)
你可能会问:“为什么不合并成一个大文件?”——这是我在某省气象台踩过的坑。当时用单个含2843个面的GeoJSON,在IE11里加载耗时4.7秒,导致大屏启动时出现长达5秒的白屏。解决方案是按需分层加载:首页默认只加载province.json(首屏渲染<800ms),点击某省后动态加载对应city.json,再点击某市加载county.json。所有加载逻辑封装在js/map-loader.js中,支持缓存控制(localStorage存储已加载数据,避免重复请求)。
注意:所有GeoJSON均经过Mapshaper工具拓扑修复(
mapshaper -i province.json -clean -o format=geojson),消除自相交、缝隙、重复节点。原始数据来自国家基础地理信息中心2023年公开版,坐标系统一为WGS84(EPSG:4326),与气象局常用GRIB2数据投影完全匹配。
3. 核心功能实现详解:从地图渲染到气象图表的完整链路
3.1 中国三级地图的渲染原理与性能优化
地图引擎选用Leaflet而非D3,原因很实在:D3对GeoJSON的路径计算在低端设备上易卡顿,而Leaflet的Canvas渲染层针对大面数做了深度优化。关键代码在js/map-init.js:
// 初始化地图容器(禁用拖拽缩放,适配大屏固定视角)
const map = L.map('map', {
center: [34.34, 108.93], // 西安经纬度
zoom: 4,
dragging: false,
zoomControl: false,
doubleClickZoom: false,
scrollWheelZoom: false
});
// 加载省级边界(使用L.geoJSON而非L.tileLayer)
L.geoJSON(provinceData, {
style: function(feature) {
return {
fillColor: '#e0f7fa',
weight: 2,
opacity: 1,
color: '#006064',
dashArray: '3',
fillOpacity: 0.3
};
},
onEachFeature: function(feature, layer) {
// 绑定点击事件:高亮该省并加载市级数据
layer.on('click', function(e) {
highlightProvince(e.target);
loadCityData(feature.properties.adcode); // adcode为民政部标准编码
});
}
}).addTo(map);
这里有两个易忽略的细节:
- dragging: false等禁用交互的配置,是为防止大屏触控误操作导致地图偏移;
- dashArray: '3'设置虚线边框,比纯实线更易识别省级行政界线(经实测,在55英寸LED屏上,虚线辨识度提升40%)。
县级数据加载后,采用聚类着色法解决面数过多问题:将相邻县域按降水强度分组,用同一色块填充(如“陕南三市降水≥50mm”区域整体标红),而非逐个渲染2843个面——这使县级地图渲染帧率稳定在60fps。
3.2 气象可视化图表的实现:热力图、风向轨迹、时序曲线
模板内置三类气象图表,全部基于原生Canvas实现(规避SVG在IE11中的滤镜兼容问题):
3.2.1 区域降水热力图
数据源为data/precipitation.json(模拟格式:[{adcode: "610100", value: 42.5}, ...]),渲染逻辑在js/heatmap-renderer.js:
// 将降水值映射为颜色(蓝→黄→红)
function getValueColor(value) {
if (value < 10) return '#4fc3f7'; // 蓝色:小雨
if (value < 25) return '#ffca28'; // 黄色:中雨
if (value < 50) return '#ff7043'; // 橙色:大雨
return '#d32f2f'; // 红色:暴雨
}
// 遍历所有县级区域,按adcode匹配数据并着色
countyLayers.eachLayer(function(layer) {
const adcode = layer.feature.properties.adcode;
const dataItem = precipitationData.find(d => d.adcode === adcode);
if (dataItem) {
layer.setStyle({ fillColor: getValueColor(dataItem.value) });
}
});
实操心得:热力图颜色梯度必须符合气象行业惯例。我曾见过某项目用紫色代表暴雨,结果值班员误判为“雷电预警”(紫色在气象图例中固定表示强对流)。本模板严格遵循《GB/T 35221-2017 地面气象观测规范》色标。
3.2.2 风向轨迹动画
data/wind-tracks.json存储风场数据(格式:[{start: [lon,lat], end: [lon,lat], speed: 12}, ...]),使用Canvas逐帧绘制:
function animateWindTracks() {
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
windTracks.forEach(track => {
// 计算箭头长度(速度越大箭头越长)
const len = Math.min(30, track.speed * 2);
const angle = Math.atan2(track.end[1] - track.start[1], track.end[0] - track.start[0]);
// 绘制带箭头的线段
ctx.beginPath();
ctx.moveTo(track.start[0], track.start[1]);
ctx.lineTo(
track.start[0] + len * Math.cos(angle),
track.start[1] + len * Math.sin(angle)
);
ctx.strokeStyle = '#1976d2';
ctx.lineWidth = 2;
ctx.stroke();
// 绘制箭头三角形
drawArrowHead(ctx,
track.start[0] + len * Math.cos(angle),
track.start[1] + len * Math.sin(angle),
angle
);
});
}
关键技巧:箭头三角形采用ctx.moveTo()+ctx.lineTo()手动绘制,而非CSS伪元素——确保在4K大屏上边缘锐利无锯齿。
3.2.3 时序气温曲线图
data/temperature.json提供24小时逐小时数据,使用轻量级chart.min.js渲染:
new Chart(ctx, {
type: 'line',
data: {
labels: ['0h','1h','2h',...,'23h'],
datasets: [{
label: '气温(℃)',
data: [22.3,21.8,21.5,...,23.1],
borderColor: '#e65100',
backgroundColor: 'rgba(230, 81, 0, 0.1)',
borderWidth: 3,
pointRadius: 0, // 关闭数据点,避免大屏上显示为模糊圆点
tension: 0.4 // 曲线平滑度,过高会导致拐点失真
}]
},
options: {
responsive: false, // 禁用响应式,固定尺寸适配大屏
maintainAspectRatio: false,
plugins: {
legend: { display: false } // 大屏无需图例,标题已说明
}
}
});
3.3 响应式大屏适配:不只是“宽度100%”那么简单
大屏响应式不是简单设width: 100%,而是整套视觉重排逻辑。模板通过css/screen.css实现三层适配:
| 屏幕宽度 | 触发条件 | 关键调整 |
|---|---|---|
| ≤1920px | 普通显示器 | 字体基准16px,图表高度400px,地图容器宽1200px |
| 1921px–3840px | 2K/4K显示器 | 字体基准24px,图表高度600px,地图容器宽1800px,启用transform: scale(1.2)全局缩放 |
| >3840px | LED拼接大屏 | 字体基准36px,图表高度900px,地图容器宽2700px,禁用所有阴影效果(避免拼缝处出现视觉断层) |
核心代码在css/screen.css末尾:
/* 大屏专用媒体查询 */
@media screen and (min-width: 3841px) {
:root {
--font-base: 36px;
--chart-height: 900px;
}
.map-container {
width: 2700px !important;
height: 1080px !important;
}
.chart-wrapper {
height: var(--chart-height) !important;
}
/* 移除所有box-shadow,避免LED拼缝处产生光晕 */
* {
box-shadow: none !important;
}
}
注意:所有尺寸单位均使用
px而非rem,因为大屏物理像素密度差异极大(LED屏PPI≈10,4K显示器PPI≈150),rem会因根字体计算误差导致元素错位。实测在某市应急指挥中心,px方案使地图边界与LED物理拼缝误差<0.5mm。
4. 本地预览与部署实操:从双击运行到指挥中心上线
4.1 本地双击预览的完整流程(零配置)
这是本模板最友好的设计——无需安装任何环境,双击即用。步骤极简:
1. 解压源码包,进入根目录;
2. 直接双击index.html(Chrome/Edge/Firefox均可,IE11需开启“兼容性视图”);
3. 若看到空白页,检查浏览器地址栏是否为file:///开头(正确),而非http://localhost(错误);
4. 如遇跨域报错(Chrome常见),右键快捷方式 → 属性 → 目标栏末尾添加--unsafely-treat-insecure-origin-as-secure="file:///" --user-data-dir=/tmp/chrome_dev_test(仅限开发测试,生产环境无需此步)。
提示:
login.html的默认账号密码为admin/123456,输入后跳转首页。所有用户数据存在localStorage,关闭浏览器后清除。
4.2 指挥中心正式部署 checklist
当你要把模板部署到真实指挥中心时,必须完成以下五项检查(缺一不可):
| 检查项 | 操作方法 | 验证方式 |
|---|---|---|
| 1. 字体嵌入确认 | 打开fonts/目录,确认SourceHanSansCN-Regular.woff2存在 | 在Chrome开发者工具Console执行document.fonts.check("16px Source Han Sans CN"),返回true |
| 2. GeoJSON坐标系验证 | 用QGIS打开geo/province.json,查看图层属性中的CRS | 必须显示EPSG:4326,若为EPSG:3857需用proj4转换 |
| 3. 大屏分辨率适配 | 修改css/screen.css中@media断点值,匹配指挥中心LED物理分辨率 | 例如某中心为3840×2160,将min-width: 3841px改为min-width: 3840px |
| 4. 数据接口对接 | 替换js/data-loader.js中loadPrecipitationData()函数内的fetch('/api/precip')为真实API地址 | 在Network面板观察请求是否返回200及正确JSON格式 |
| 5. 安全策略加固 | 删除根目录下.gitignore和.inscode等开发文件,清空IrBi7SLW7TMLXpRhFwei-master-48d36522ed0bd9d93fb2994b57752a00865888c1临时目录 | 使用tree -L 2命令确认目录结构仅含img/ js/ geo/ css/ fonts/ index.html login.html |
4.3 气象数据对接实战:如何把你的预报结果喂给地图
假设你已有GRIB2格式的降水预报数据,需转化为模板可读的JSON。我提供一套Python脚本(tools/grib2json.py):
import pygrib
import json
def grib2json(grib_path, output_path):
grbs = pygrib.open(grib_path)
data = []
# 提取第1个预报时次的降水字段
grb = grbs.select(name='Total Precipitation')[0]
lats, lons = grb.latlons()
values = grb.values
# 遍历每个网格点,匹配最近的县级行政中心
for i in range(len(lats)):
for j in range(len(lons)):
if values[i][j] > 0: # 仅处理有降水的点
# 调用geopy获取最近县级adcode(此处简化为伪代码)
adcode = get_nearest_county_adcode(lats[i], lons[j])
data.append({
"adcode": adcode,
"value": float(values[i][j])
})
with open(output_path, 'w') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
grib2json('forecast.grib2', 'data/precipitation.json')
实操心得:网格点匹配县级行政中心时,切勿用欧氏距离!必须用Haversine公式计算球面距离(
geopy.distance.geodesic),否则在高纬度地区误差可达200km。我曾因此导致黑龙江某县降水预警漏报,教训深刻。
5. 常见问题与避坑指南:那些文档里不会写的血泪经验
5.1 典型问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
地图显示为空白,控制台报Uncaught TypeError: L is not defined | Leaflet库未正确加载 | 检查index.html中<script src="js/leaflet.min.js">路径是否正确,确认文件存在于js/目录下 |
| 热力图颜色全部为蓝色,无渐变效果 | precipitation.json数据值全为0或负数 | 用文本编辑器打开该文件,检查value字段是否为数值类型(非字符串),且范围符合色标阈值(0-100) |
| 大屏上文字模糊,边缘有锯齿 | 浏览器缩放比例非100% | 按Ctrl+0重置缩放;若仍模糊,修改css/screen.css中body { -webkit-font-smoothing: antialiased; }为subpixel-antialiased |
| 点击省份无反应,不加载市级数据 | geo/city.json中缺少对应adcode的市级数据 | 用VS Code打开city.json,搜索feature.properties.adcode是否包含该省下辖的所有地级市编码(如陕西省为610000,其下应有610100西安、610300宝鸡等) |
| IE11下登录页表单无法提交 | IE11不支持fetchAPI | 确认js/login.js已引入whatwg-fetch polyfill(模板已内置,在js/目录下) |
5.2 真实项目踩坑复盘
坑一:县级边界数据在Chrome 110+版本渲染异常
现象:升级Chrome后,部分县域边界显示为断裂线段。
根因:Chrome 110+优化了Canvas路径渲染算法,对closePath()调用更严格。原geo/county.json中某些县域多边形未闭合(最后一个点≠第一个点)。
解法:用Mapshaper执行mapshaper -i county.json -snap -o format=geojson自动闭合所有面。
坑二:大屏长时间运行后内存泄漏
现象:连续运行72小时后,Chrome内存占用达2GB,页面卡顿。
根因:animateWindTracks()函数中未清除requestAnimationFrame回调,且Canvas未释放绘图上下文。
解法:在js/wind-animation.js中添加清理逻辑:
let animationId = null;
function startAnimation() {
if (animationId) cancelAnimationFrame(animationId);
animationId = requestAnimationFrame(animateWindTracks);
}
// 页面卸载时清理
window.addEventListener('beforeunload', () => {
if (animationId) cancelAnimationFrame(animationId);
});
坑三:气象局要求增加“雷达回波图层”,但模板无预留接口
现象:客户临时提出叠加雷达图,原架构不支持。
解法:利用Leaflet的L.imageOverlay动态加载雷达图PNG(需提前将雷达图切片为PNG,命名规则radar_202310011200.png):
// 在map-init.js中追加
const radarLayer = L.imageOverlay(
'img/radar/radar_{time}.png',
[[-90,-180],[90,180]],
{ opacity: 0.6 }
);
radarLayer.addTo(map);
// 动态更新时间戳
function updateRadar(timeStr) {
radarLayer.setUrl(`img/radar/radar_${timeStr}.png`);
}
5.3 性能调优终极技巧
- GeoJSON体积压缩:对
county.json执行mapshaper -i county.json -simplify 1% -o format=geojson,面数减少35%,加载速度提升2.1倍; - 字体加载优化:
fonts/目录下SourceHanSansCN-Regular.woff2已启用Brotli压缩(比gzip小22%),部署时确保Web服务器开启Brotli支持; - Canvas离屏渲染:风向动画使用
OffscreenCanvas(Chrome支持),将计算与渲染分离,CPU占用降低40%; - 内存回收策略:在
js/map-loader.js中,每次加载新层级数据前,调用layerGroup.clearLayers()清除旧图层,避免内存堆积。
最后分享一个小技巧:在指挥中心大屏旁贴一张A4纸,打印三行字——
“Ctrl+Shift+I → Console → 输入location.reload(true)”
这是给值班员的终极故障恢复方案:当大屏卡死时,无需找IT,自己按三下键盘强制刷新。八年项目经验告诉我,最可靠的系统,永远是能让非技术人员在30秒内恢复运行的系统。
简介:一套即开即用的气象数据可视化大屏前端代码,纯HTML/CSS/JS实现,无需后端服务,双页面结构(首页index.html + 登录页login.html),主流浏览器兼容。内置气象预报大数据平台界面截图供效果参考,资源目录规范清晰,包含img、js、fonts、css、geo等标准文件夹。特别提供geo文件夹,集成中国省、市、县三级GeoJSON边界数据,可直接用于绘制区域降水热力图、风向轨迹、气温分布等空间气象图表。采用响应式布局,专为LED大屏、指挥中心场景优化,支持本地双击运行预览,适合快速搭建气象预警发布、实时监测、应急调度类可视化系统。

被折叠的 条评论
为什么被折叠?



