当前位置: 首页 > Web前端 > HTML5

基于HTML5Canvas的3D热云图效果

时间:2023-04-05 11:11:03 HTML5

前言 数据蕴含价值,但数据的价值需要借助IT技术去发现和挖掘。可视化可以帮助人们更好地分析数据,而信息的质量很大程度上取决于它的呈现方式。在数据分析中,热力图无疑是一种很好的方式。它广泛应用于许多行业。最近项目需要用到3D热力图的效果展示。在网上搜索了相关资料,发现大部分都是2D效果或者伪3D,3D粒子效果对于性能体验来说不是很好,所以偶然写了一个3D热图效果。  Demo:http://www.hightopo.com/demo/heatMap3D/  局部效果图:应用场景建筑人员分布热力图。我们可以通过观察一个区域的颜色深浅来判断一个区域的实时人流量,知道哪个区域人多,哪个区域人少。该场景可应用于楼宇内的警务监控。突发事件发生时,科学高效制定分流策略,提供有力帮助和支持,减少损失。它还可以用于火灾危险预警和区域实时温度监测。室内设备温度热力图。传统的数据中心报表方式枯燥单调,真实感低,交互性差。借助3D热力图的可视化呈现,机房运维管理人员可以大大提高工作效率,减少工作失误的可能性。总体思路是在场景反序列化后设置热图的初始参数,将初始化后得到的热图模型添加到场景中,模拟3D热图的效果,最后添加扫描、蒙皮、和温度提示。1.数据准备绘制场景中热图的区域,如图,首先确定生成热图的areaNode,然后随机生成20个点的信息,包括坐标位置(坐标为相对于红色长方体的顶点)和热值temperature。  以下是这部分的主要代码:functiongetTemplateList(areaNode,hot,num){letheatRect=areaNode.getRect();让{宽度,高度}=heatRect;让rackTall=areaNode.getTall();热=热+this.random(20);让templateList=[];for(leti=0;i{if(e.kind==='onMove'){let{clientX,clientY}=e.event;if(this.templateTip){letvalue1=this.thd1.getHeatMapValue(e.event,'middle');letvalue2=this.thd2.getHeatMapValue(e.event,'middle');if(value1||value1===0||value2||value2===0){letposition=g2d.getLogicalPoint({x:clientX,y:clientY});this.templateTip.a('value',value1||value2||0);let{width,height}=this.templateTip.getRect();this.templateTip.setPosition({x:position.x+width/2,y:position.y-height/2});}}}elseif(kind==='onLeave'){lettag=data.getTag();if(tag&&tag.hasOwnProperty('hoverBlock')>-1){this.g2d.getView().style.cursor='default';}this.templateTip&&this.setVisible(this.templateTip,false);}})5.Scan替换第三步中的thd.createThermodynamicNode()。在生成热力图对象时,并不是直接返回一个模型,而是选择某个方向进行“切割”,将这个方向的长度分成n份,使用thd.getHeatMap()方法获取热图每一块。n的值理论上可以取任意值,但是为了更好的渲染效果,我这里取50,不会太多导致第一次渲染时间过长。每次切出一个面,我们在热区的相对位置动态创建一个ht.Node,然后使用ht.Default.setImage()将切出的面注册为图片,并设置为贴图节点(只需将两个面设置为切割方向即可)。最后将所有节点添加到dataModel(ht中承载Data数据的模型)。  扫描功能,有两种选择。第一种是在第3步切割patch时不创建n个节点,只创建一个,然后动态设置节点的纹理和坐标,模拟扫描效果;第二个还是创建n个节点,然后全部隐藏,控制其中一个节点在不同时间显示,模拟扫描功能。这里我用的是第二种,因为第一种需要经常修改多个属性才能达到效果,而第二种只需要控制它的‘3d.visible’即可。  主要代号如下:letlength;if(dir==='z'){length=rackTall;}elseif(dir==='x'){length=width;}elseif(dir==='y'){length=height;}让size=config.size;for(letindex=0;index{letctx=thd.getHeatMap(index*offset,dir,colorConfig);letfloor=this.getHeatFloor(areaNode,dir,ctx,index,size,config);this.floors.推(地板);dm.add(地板);},0);this.timers.push(timer);}functionstart(){this.hide();这个.anim=true;这个。计数=0;让帧=this.floors.length;letparams={frames,//动画帧数interval:50,//动画帧间间隔秒数easing:t=>{returnt;},finishFunc:()=>{if(this.anim){this.start();}},动作:(v,t)=>{this.count++;这个。显示(这个。计数);}};this.scanning=ht.Default.startAnim(params);}functionhide(index){if(index||index===0){this.floors.forEach((i,j)=>{if(index===j){i.s('3d.visible',false);}else{i.s('3d.visible',true);}});}else{this.floors.forEach(i=>{i.s('3d.visible',false);});}}functionshow(index){if(index||index===0){this.floors.forEach((i,j)=>{if(index===j){i.s('3d.visible',true);}else{i.s('3d.visible',false);}});}else{this.floors.forEach(i=>{i.s('3d.visible',true);});}}第一种方法实现主要代码:getHeatFloor(node,dir,config){let{width,height}=node.getRect();让rackTall=node.getTall();让s3=[1,rackTall,height];让floor=newht.Node();floor.setTag('热点');floor.setAnchor3d({x:0.5,y:0.5,z:0.5});floor.s3(s3);floor.s({interactive:true,preventDefaultWhenInteractive:false,'3d.selectable':true,'3d.movable':false,'all.visible':false,[Top+'.visible']:true,[Top+'.opacity']:config.opacity,[Top+'.transparent']:true,[Top+'.reverse.flip']:true,[Top+'.color']:'rgba(51,255,231,0.10)'});返回楼层;}getHeatFloorInfo(node,dir,ctx,index,size,config){let{width,height}=node.getRect();让rackTall=node.getTall();让point=node.getPosition3d();让部分=0;让p3,s3;让顶部='顶部';如果(!dir){dir='z';}//热力图的yz方向与ht的yz方向相反dir=z表示垂直方向if(dir==='x'){Top='left';部分=(宽度/尺寸)*索引;p3=[point[0]-width/2+part,point[1]+rackTall/2,point[2]];//p3=[point[0]+part,point[1],point[2]];s3=[1,rackTall,高度];}elseif(dir==='y'){Top='front';部分=(高度/尺寸)*索引;p3=[point[0],point[1]+rackTall/2,point[2]-height/2+part];s3=[宽度,rackTall,1];}elseif(dir==='z'){Top='top';部分=(rackTall/size)*index;p3=[点[0],点[1]+部分,点[2]];s3=[宽度,1,高度];}让heatName=this.generateUUID();ht.Default.setImage('heatMap'+heatName,ctx);this.heatFloorInfo.push({img:'heatMap'+heatName,p3})}show(index){letinfo=this.heatFloorInfo[index];this.floor.p3(info.p3);吨his.floor.s('3d.visible',true);this.floor.s('top.image',info.img);//手动刷新this.floor.iv();}6.换肤  换肤的实现原理:根据不同的场景值动态修改ht.graph3d.Graph3dView的背景色和墙色  代码:functionchangeSkin(){letbackgroundColor=this.g3d.DM()。getBackground(),dark_bg=this.g3d.dm().getDataByTag('dark_skin'),light_bg=this.g3d.dm().getDataByTag('light_skin');if(backgroundColor!=='rgb(255,255,255)'){this.g3d.dm().setBackground('rgb(255,255,255)');}else{this.g3d.dm().setBackground('rgb(0,0,0)');}dark_bg.s('2d.visible',!dark_bg.s('2d.visible'));dark_bg.s('3d.visible',!dark_bg.s('3d.visible'));light_bg.s('2d.visible',!light_bg.s('2d.visible'));light_bg.s('3d.visible',!light_bg.s('3d.visible'));}  是本文介绍的,目前ht-thermodynamic.js还处于内测阶段,等相对成熟后会更新demo。如果你有兴趣了解更多关于2D/3D可视化的构建,你可以阅读其他文章中的例子。HT会给你很多不可思议的东西。