您现在的位置是:网站首页> 编程资料编程资料
vue3+three.js实现疫情可视化功能_vue.js_
2023-05-24
1363人已围观
简介 vue3+three.js实现疫情可视化功能_vue.js_
前言
自成都九月份以来疫情原因被封了一两周,居家着实无聊,每天都是盯着微信公众号发布的疫情数据看,那种页面,就我一个前端仔来说,看着是真的丑啊!(⊙_⊙)?既然丑,那就自己动手开整!项目是2022.9.5开始的,截止2022.9.12我完成了大概有八成。主要是想让数据更加直观,而且可离线下载(当然还有装逼!┑( ̄Д  ̄)┍)。
项目描述
为证明是有料的,先看效果图(提前装逼!┗|`O′|┛ 嗷~~):




项目我是公开了的( ̄m ̄)有兴趣的可以下下来玩玩,这是我第一次使用vue3+ts构建项目,肯定还有不足的地方(比如ts中疯狂的:any,一直any一直爽^o^/)。
这里是在线链接
这里是项目链接(欢迎star!欢迎star!欢迎star!(●'◡'●)嘿嘿嘿~)
项目中使用到的技术有:vue3、TypeScript、Three.js、Echarts、elementPlus。
项目目标
1、以为3D形式展示全球疫情分布。
2、显示实时疫情数值。
3、以图表形式分析疫情数据。
4、允许下载各地疫情excel表格。
5、自动获取用户位置。
6、分析当地疫情数据。
7、生成当地疫情word报告。
api说明
本项目数据来源:新浪公共疫情api(新浪的数据来源于国家卫健委、各省市区卫健委、各省市区政府、港澳台官方渠道等公开数据。这也是够权威官方了)。我主要使用了两个新浪的api和一个太平洋网络ip地址查询web接口。
1、https://news.sina.com.cn/project/fymap/ncp2020_full_data.json
get方式,无入参。该api可获取全球各国大致疫情数据,以及国内的详情疫情数据,这里就api中的字段做一下说明,字段是我自己推测出来的含义,不会100%全而准(→_→):

{ "add_daily(国内今日数据)": { "addcon(今日确诊新增数)": "", "addcure(今日治愈新增)": "", "adddeath(今日死亡新增)": "", "addjwsr(今日境外输入新增)": "", "addlocIncrNum(今日本土新增)" }, "cachetime(数据缓存时间)": "", "curetotal(国内治愈总数)": "", "deathtotal(国内死亡总数)": "", "gntotal(国内确诊总数)": "", "highAndMiddle(中高风险地列表)": [ { "allname(全名)": "", "list(城市列表)": "", "province(省名)": "", "province_high_areas(高风险区域)": "", "province_middle_areas(中风险区域)": "", "province_high_num(高风险区域数)": "", "province_middle_num(中风险区域数)": "", "province_total(风险地总数)": "" } ], "historylist(国内疫情历史数据)": [ { "cn_conNum(确诊总数)": "", "cn_cureNum(治愈总数)": "", "cn_deathNum(死亡数)": "", "cn_jwsrNum(境外输入)": "", "ymd(当前时间)": "" } ], "jwsrTop(境外数据前10列表)": [], "list(全国各省疫情数据列表)": [ { "asymptomNum(较昨日新增数)": "", "city(城市列表)": [], "cureNum(治愈数)": "", "deathNum(死亡数)": "", "econNum(现存确诊数)": "", "ename(英文省名)": "", "jwsrNum(境外输入数)": "", "name(省名)": "", "value(累计数)": "" } ], "locIncrProTop(本土新增前十列表)": [], "othertotal(其他总数)": { "certain(全球现存确诊)": "", "certain_inc(今日确诊新增数)": "", "die(全球死亡数)": "", "die_inc(死亡新增数)": "", "ecertain(全球治愈数)": "", "ecertain_inc(治愈新增数)": "" }, "times(数据截止时间)": "", "worldlist(世界各国疫情列表)": [ { "name(国名)": "", "value(累计数)": "", "econNum(确诊数)": "", "deathNum(死亡数)": "", "cureNum(治愈数)": "" } ] }2、https://gwpre.sina.cn/interface/news/ncp/data.d.json
get方式,入参:
{ mod:"province", province(英文省名):"" }该api可获取国内指定省份的疫情数据,字段我就不推断了,可自行根据上一个api和部分英文单词大概推断出来(没错!就是我不想打字了!太TM累了!ಥ_ಥ其实这还不算折磨人的,后面使用api的时候那才叫个曲折)。
3、https://whois.pconline.com.cn/ipJson.jsp
get方式,无入参。该api可获取使用者ip地址、省份、城市。返回结果如下:

数据使用
刚开始开发的时候,我跟以前项目开发一样,跨域嘛,直接整个vue代理(不会vue代理模式的看这儿)不就完事儿了,果然,数据一经过代理,回来是回来了,但汉字全是\n什么什么鬼?乱码?费了一番功夫查了下,不是乱码,是unicode解码的问题。然后我又整了个解码的方法:
//解码返回的unicode function decodingStr(str: any) { let repStr: any = str.replace(/\\/g, "%");//用%替换\ let str1 = repStr.split("jsoncallback(")[1] let str2 = str1.split(");")[0]//截取出需要的字符串 let unStr = unescape(str2);//解码出汉字 let jsonObj = JSON.parse(unStr);//转换成json对象 return jsonObj; };这下应该可以了吧?一切很顺利,开发差不多了,npm run build、git add . 、git commit -m""、git push,行云流水!直接上gitee Pages部署发布,完成!打开页面一看?卧槽?f12。404?直到后面我又在网上扒拉后才明白,vue的代理在打包成dist后会被抽离失效,在gitee Pages中是不能使用vue的代理模式获取数据的!接下来就是各种尝试跨域,直到看到跨域两个字人都麻了。最后发现不同域下,使用jsonp的方式来处理跨域最为简单,jsonp原理和使用方法在这里。
项目开始
项目是vue3的,首先你得创建啊。这里建议使用vue脚手架的图形化界面创建项目,命令为:vue ui
选择手动配置:

打开TypeScript支持:

选择vue3选项:

安装依赖
1、npm install echarts@4.9.0(安装echarts的指定版本,因为项目中需要使用中国地图,在4.9.0之后echarts官方移除了地图支持,之后的版本需要下载chain.js,还得手动下载引入一遍,麻烦,这里直接用老版本)
2、npm i element-plus(vue3对应的element-ui就是element-plus,这是官方使用文档)
3、npm i three(看见首页那个大地球了吧?没错,它就是three.js做的,感兴趣的可以看这儿,还有个太阳系)
4、npm i xlsx(这个是下载excel表格的必备插件,具体使用方法看这里)
首页球体
1、创建宇宙(叼不叼!是不是感觉自己就是创世主!( ̄_, ̄ )):初始化场景时一定记得设置alpha: true。这里创建宇宙我使用了这篇文章创建背景的第三种方法。
import * as THREE from "three"; //初始化球体 function init(data: any) { dom = document.getElementById("sphereDiv"); //获取dom let width = dom.clientWidth; let height = dom.clientHeight; scene = new THREE.Scene(); //场景场景 camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000); //创建透视相机(视场、长宽比、近面、远面) camera.position.set(0, 0, 270); //设置相机位置 camera.lookAt(0, 0, 0); //创建渲染器 renderer = new THREE.WebGLRenderer({ antialias: true, //抗锯齿 alpha: true, //透明 }); renderer.setClearColor(0x000000, 0.1); //设置场景透明度 renderer.setSize(width, height); //设置渲染区域尺寸 dom.appendChild(renderer.domElement); //将渲染器添加到dom中形成canvas createUniverse(); //创建宇宙 createStars(); //创建星辰 createLight(); //创建光源 createSphere(data); //创建球体 createOrbitControls(); render(); }; //创建宇宙(球形宇宙) function createUniverse() { let universeGeometry = new THREE.SphereGeometry(500, 100, 100); let universeMaterial = new THREE.MeshLambertMaterial({ //高光材质 map: new THREE.TextureLoader().load(universeImg), side: THREE.DoubleSide, //双面显示 }); //宇宙网格 let universeMesh = new THREE.Mesh(universeGeometry, universeMaterial); universeMesh.name = "宇宙"; scene.add(universeMesh); };2、创建光源:为了效果,我使用了环境光与平行光源,这两种光都会影响贴图原本颜色,建议光源颜色设置为白色。
//创建光源 function createLight() { let lightColor = new THREE.Color(0xffffff); let ambient = new THREE.AmbientLight(lightColor); //环境光 ambient.name = "环境光"; scene.add(ambient); let directionalLight1 = new THREE.DirectionalLight(lightColor); directionalLight1.position.set(0, 0, 1000); scene.add(directionalLight1); //平行光源添加到场景中 let directionalLight2 = new THREE.DirectionalLight(lightColor); directionalLight2.position.set(0, 0, -1000); scene.add(directionalLight2); //平行光源添加到场景中 let directionalLight3 = new THREE.DirectionalLight(lightColor); directionalLight3.position.set(1000, 0, 0); scene.add(directionalLight3); //平行光源添加到场景中 let directionalLight4 = new THREE.DirectionalLight(lightColor); directionalLight4.position.set(-1000, 0, 0); scene.add(directionalLight4); //平行光源添加到场景中 let directionalLight5 = new THREE.DirectionalLight(lightColor); directionalLight5.position.set(0, 1000, 0); scene.add(directionalLight5); //平行光源添加到场景中 let directionalLight6 = new THREE.DirectionalLight(lightColor); directionalLight6.position.set(0, -1000, 0); scene.add(directionalLight6); //平行光源添加到场景中 };3、创建球体:
//创建球体 function createSphere(data: any) { let earthSize = 100; //地球尺寸 let earthGroup = new THREE.Group(); //地球的组 let earthGeometry = new THREE.SphereGeometry(earthSize, 100, 100); //地球几何体 let nightColor = new THREE.Color(0x999999); let dayColor = new THREE.Color(0x444444); //地球材质 let earthMaterial = new THREE.MeshPhongMaterial({ map: new THREE.TextureLoader().load( isDay ? earthImg : earthNightImg //区分昼夜纹理 ), color: isDay ? dayColor : nightColor, // metalness: 1, //生锈的金属外观(MeshStandardMaterial材质时使用) // roughness: 0.5, // 材料的粗糙程度(MeshStandardMaterial材质时使用) normalScale: new THREE.Vector2(0, 5), //凹凸深度 normalMap: new THREE.TextureLoader().load(normalImg), //法线贴图 }); let earthMesh = new THREE.Mesh(earthGeometry, earthMaterial); //地球网格 earthMesh.name = "地球"; earthGroup.add(earth
相关内容
- 使用vxe-table合并单元格后增加选中效果_vue.js_
- JS实现单例模式的N种方案_javascript技巧_
- 如何去掉ElementUI Table的hover变色问题_vue.js_
- 一文带你玩转JavaScript的箭头函数_javascript技巧_
- vxe-table如何在单元格中渲染简单的饼图_vue.js_
- vue嵌入本地iframe文件并获取某元素的值方式_vue.js_
- Node.js 中的 module.exports 与 exports区别介绍_node.js_
- 关于vxe-table复选框翻页选中问题及解决_vue.js_
- 如何解决element-ui动态加载级联选择器默认选中问题_vue.js_
- vue Tooltip提示动态换行问题_vue.js_
点击排行
本栏推荐
