全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-11-14_高德地图+Three.js实现飞线、运动边界和炫酷标牌

您的位置:首页 >> 新闻 >> 行业资讯

高德地图+Three.js实现飞线、运动边界和炫酷标牌 点击关注公众号,“技术干货”及时达! 地图经常使用标牌来标注地点位置,是时候来点炫酷的效果了!用高德地+Three.js,带你实现飞线、运动边界和炫酷标牌。 1.高德地图+Three.js可以参考官方示例高德地图-自定义图层-GLCustomLayer 结合 THREE 初始化高德3D地图this.map=newAMap.Map(this.container,{ //缩放范围 zooms:[2,20], zoom:4.5, //倾斜角度 pitch:0, //隐藏标签 showLabel:false, //3D地图模式 viewMode:'3D', //初始化地图中心点 center:this.center, //暗色风格 mapStyle:'amap://styles/dark' 自定义高德地图经纬度转px工具,即墨卡托投影坐标转换。//数据转换工具 this.customCoords=this.map.customCoords; //设置坐标转换中心 this.customCoords.setCenter(this.center); 高德地图添加WebGL自定义图层,初始化Three.js配置//创建GL图层 vargllayer=newAMap.GLCustomLayer({ //zIndex:10, //初始化的操作,创建图层过程中执行一次。 init:(gl)={ //初始化Three相机 this.camera=newTHREE.PerspectiveCamera( 60, this.container.offsetWidth/this.container.offsetHeight, 1, 130 //初始化Three渲染器 this.renderer=newTHREE.WebGLRenderer({ context:gl//地图的gl上下文 this.renderer.setPixelRatio(window.devicePixelRatio); //自动清空画布这里必须设置为false,否则地图底图将无法显示 this.renderer.autoClear=false; //初始化场景 this.scene=newTHREE.Scene(); this.createChart(); }, render:()={ //这里必须执行!!重新设置 three 的 gl 上下文状态。 this.renderer.resetState(); //设置坐标转换中心 this.customCoords.setCenter(this.center); var{near,far,fov,up,lookAt,position}=this.customCoords.getCameraParams(); //这里的顺序不能颠倒,否则可能会出现绘制卡顿的效果。 this.camera.near=near; this.camera.far= this.camera.fov= this.camera.position.set(...position); this.camera.up.set(...up); this.camera.lookAt(...lookAt); this.camera.updateProjectionMatrix(); //渲染器渲染场景 this.renderer.render(this.scene,this.camera); //这里必须执行!!重新设置 three 的 gl 上下文状态。 this.renderer.resetState(); } this.map.add(gllayer); 2.绘制运动边界行政区边界可以通过地理小工具获取。 高德地图折线polyline经常使用,但是让折线运动起来,你可能很少其实只需要添加几行代码就能实现。 实现逻辑:添加边界折线,然后截取折线的一段,让它跟着时间往后移动,即开始索引递增,截取相同折线,即可让折线运动起来。 //绘制中国大陆运动边界 createChinaLine(){ returnnewPromise((resolve)={ fetch('https://geo.datav.aliyun.com/areas_v3/bound/100000.json') .then((res)=res.json()) .then((res)={ letpath=res.features[0].geometry.coordinates[0][0]; //截取10%的线段 letlen=Math.floor(path.length*0.1); //边界折线 letpolyline=newAMap.Polyline({ path:path, strokeWeight:4, strokeColor:'white', lineJoin:'round', strokeOpacity:1 this.map.add(polyline); //利用Tween创建动画 newTWEEN.Tween({start:0}) .to({start:path.length},3000) //无限循环动画 .repeat(Infinity) .onUpdate((obj)={ if(obj.start+lenpath.length){ polyline.setPath(path.slice(obj.start,obj.start+len)); }else{ constc=path.length-obj.start; //头尾相接时截取尾部+头部各一段 polyline.setPath( [].concat(path.slice(obj.start,path.length),path.slice(0,len-c)) } }) .start(); resolve(); } //Tween动画 animateAction(){ if(TWEEN.getAll().length){ TWEEN.update(); } } 运动边界实现简单,但效果一级棒!并且2D和3D地图都能用,优秀! 3.绘制升起山峰在对应的坐标点上升起一座尖尖的山峰,这里使用Plane平面实现 顶点着色器precisionmediumpfloat; uniformfloatuTime; uniformfloatuHeight; varyingfloat floatPI=acos(-1.0); vec2center=vec2(0.5); voidmain(void){ //离中线的距离 floatd=length(uv-center)*2.0; //沿中心点往外减少 vD=pow(1.0-d,3.0); //山峰高度,随着uTime变化 floath=vD*uHeight*uTime; vec3pos=vec3(position.x*0.5,position.y*0.5, gl_Position=projectionMatrix*modelViewMatrix*vec4(pos,1.0); } 片元着色器precisionmediumpfloat; uniformvec3uColor; varyingfloat voidmain(void){ if(vD0.01)//透明度太小则不渲染颜色 discard; else//透明度随着距离中心点的变化 gl_FragColor=vec4(uColor,vD*2.0); } 添加shaderMaterial的平面constmaterial=newTHREE.ShaderMaterial({ uniforms:{ //随时间变化 uTime:{value:0}, //高度 uHeight:{value:this.size}, //颜色 uColor:{value:newTHREE.Color(data.color)} }, //开启透明度 transparent:true, vertexShader:``, fragmentShader:`` this.material=material; //平面形状,方便复用 if(!this.ageometry){ //平面的面数一定要足够才能形成山峰 constgeometry=newTHREE.PlaneGeometry(this.size,this.size,500,500); this.ageometry=geometry; } constplane=newTHREE.Mesh(this.ageometry,material); //转换经纬度作为px constd=this.customCoords.lngLatToCoord(data.pos); plane.position.set(d[0],d[1],0); this.scene.add(plane); 「注意:」 平面的面数一定要足够才能形成山峰平面的大小和高度要足够大,在中国地图上才能看到,这里的this.size=500000,500k。在小的地图范围内,大小也要对应缩小。高德地图坐标与three.js的坐标有些许不同,经纬度lng,lat对应xy,而高度对应z坐标。添加山峰升起动画addAnimate(start,end,time,update){ returnnewPromise((resolve)={ consttween=newTWEEN.Tween(start) .to(end,time) .onUpdate(update) .easing(TWEEN.Easing.Quadratic.In) .onComplete(()={ resolve(tween); }) .start(); } consttw=awaitthis.addAnimate({time:0},{time:1},1000,(obj)={ this.material.uniforms.uTime.value=obj.time; //播放完删除动画 TWEEN.remove(tw); 4.绘制浮动四棱锥//转换经纬度坐标 constd=this.customCoords.lngLatToCoord(data.pos); constr=this.size*0.1; //四棱锥图形,方便复用 if(!this.cgeometry){ constgeometry=newTHREE.ConeGeometry(r,r*2,4,1); this.cgeometry=geometry; } constmaterial=newTHREE.MeshLambertMaterial({color:newTHREE.Color(data.color) //创建四棱锥网格 constcone=newTHREE.Mesh(this.cgeometry,material); //旋转90度,让四棱锥倒立 cone.rotateX(-Math.PI*0.5); //设置位置 cone.position.set(d[0],d[1],this.size*1.1); this.scene.add(cone); //收集四棱锥 this.cones.push({obj:cone,step:this.speed 设置垂直高度坐标,让四棱锥上下浮动:遇到最大或最小高度时改变速度speed方向animateAction(){ if(this.cones.length){ this.cones.forEach((c)={ //高低浮动 if(c.obj.position.z=this.maxHeight){//最大高度 c.step=-this.speed; }elseif(c.obj.position.z=this.minHeight){//最小高度 c.step=this.speed; } c.obj.position.z+=c.step; } } 5.绘制文本标牌原本尝试用Marker的自定义内容content来实现html标签的,但文档上说Marker高度属性height只有在2.1版本生效,但目前只有2.0版本,暂时无法使用高度属性 通过咨询高德地图官方,官方推荐用Loca数据可视化里面的ZMarkerLayer 以下代码来源于高德地图-Loca API标牌点-某片区房价信息 vartriangleZMarker=newLoca.ZMarkerLayer({ loca:loca, zIndex:119, depth:false, triangleZMarker.setSource(geo);//设置数据集 triangleZMarker.setStyle({ content:(i,feat)={//html自定义内容 return( 'divstyle="width:120px;height:120px;background:url(https://a.amap.com/Loca/static/loca-v2/demos/images/triangle_' +(feat.properties.price60000?'blue':'yellow') +'.png);"/div' }, unit:'meter', rotation:0, alwaysFront:true, size:[60,60], altitude:15,//高度 triangleZMarker.addAnimate({//动画 key:'altitude', value:[0,1], random:true, transform:1000, delay:2000, yoyo:true, repeat:999999, ZMarker添加整一个图层Layer生成一批Marker,官方的示例很漂亮。因为我想控制一个个的Marker,故改用Three.js的CSS2DRender来实现。 在GLCustomLayer的init中添加初始化CSS2DRenderletlabelRenderer=newCSS2DRenderer(); labelRenderer.setSize(container.offsetWidth,container.offsetHeight); labelRenderer.domElement.style.position='absolute'; labelRenderer.domElement.style.top='0px'; //不妨碍界面上点击冲突 labelRenderer.domElement.style.pointerEvents='none'; this.container.appendChild(labelRenderer.domElement); this.labelRenderer=labelRenderer; 在在GLCustomLayer的init中添加CSS2DRender渲染this.labelRenderer.render(this.scene,this.camera); 添加Label标牌addLabel(dom,pos){ //label的dom可以触发事件 dom.style.pointerEvents='auto'; constlabel=newCSS2DObject(dom); label.position.set(...pos); this.scene.add(label); returnlabel; } addALabel(data){ constdiv=document.createElement('div'); div.innerHTML=`divclass="tip-box"style="background:${data.bg};--base-color:${data.color}"span class="circle"/spanspan class="text"${data.name}/span/div`; //坐标转换 constd=this.customCoords.lngLatToCoord(data.pos); constlabel=this.addLabel(div,[d[0],d[1],this.size*1.5]); } 标牌动画和样式直接用css//变大弹出 @keyframesbig{ 0%{ transform:scale(0); } 100%{ transform:scale(1); } } //闪烁 @keyframesflash{ 0%{ opacity:0.3; } 100%{ opacity:1; } } .tip-box{ --base-color:dodgerblue; border:solid1pxvar(--base-color); background-color:rgba(30,144,255,0.3); color:white; white-space:nowrap; padding-left:8px; padding-right:16px; height:32px; animation:big1sease-in; border-radius:16px; display:flex; align-items:center; box-shadow:008pxvar(--base-color); } .tip-box.circle{ background-color:var(--base-color); height:16px; width:16px; border-radius:50%; animation:flash0.5sease-inalternateinfinite; } .tip-box.text{ margin:08px; } 「注意」:DOM对象上如果有动画css请在外部包裹一层,避免css样式冲突,导致动画失效。 6.绘制飞线飞线由管道形状绘制,通过着色器来改成头大尾小的形状。 顶点着色器floatPI=acos(-1.0); varyingfloat varyingfloat uniformfloatuTime; uniformfloatuSize; uniformfloatuLen; voidmain(void){ //取模循环 floatd=mod(uv.x-uTime,1.0); //截取uLen长度 vS=smoothstep(0.0,uLen, //不在范围内不渲染 if(vS0.01||duLen) return; //头大尾小的飞线坐标点 vec3pos=position+normal*sin(PI*0.5*(vS-0.6))*uSize; gl_Position=projectionMatrix*modelViewMatrix*vec4(pos,1.0); } 片元着色器varyingfloat uniformvec3uColor; voidmain(void){ //透明度随着飞线头尾变小 gl_FragColor=vec4(uColor, } 添加管道飞线,用贝塞尔曲线形成弯曲addLine(posList,color){ //经纬度坐标点统一转换 constd=this.customCoords.lngLatsToCoords(posList); //贝塞尔曲线形成弯曲 constcurve=newTHREE.QuadraticBezierCurve3( newTHREE.Vector3(d[0][0],d[0][1],0), //取中间点 newTHREE.Vector3((d[0][0]+d[1][0])*0.5,(d[0][1]+d[1][1])*0.5,this.size), newTHREE.Vector3(d[1][0],d[1][1],0) constgeometry=newTHREE.TubeGeometry(curve,32,10000,8,false); constmaterial=newTHREE.ShaderMaterial({ uniforms:{ //随时间变化 uTime:{value:0.0}, //飞线长度 uLen:{value:0.6}, //飞线宽度 uSize:{value:10000}, //飞线颜色 uColor:{value:newTHREE.Color(color)} }, //开启透明度 transparent:true, vertexShader:``, fragmentShader:`` constline=newTHREE.Mesh(geometry,material); this.scene.add(line); } 添加飞线动画,让飞线动起来newTWEEN.Tween({time:0}) .to({time:1.0},1000) //重复动画 .repeat(Infinity) .onUpdate((obj)={ material.uniforms.uTime.value=obj.time; }) .start(); 7.动画连续起来最终效果:地图绘制运动边界,镜头放大倾斜,对应地点长出尖尖的山峰,然后弹出一个上下浮动四棱锥和文本标牌,随即生出一条飞线,镜头跟随,跳到下一个景点,再次弹出四棱锥和文本标牌,重复,走过所有地点后,定格到最终视角。 //运动边界线 awaitthis.createChinaLine(); //绘制山峰 awaitthis.createA(this.tags[0]); //视角变化 this.map.setPitch(68,false,3000); this.map.setRotation(24,false,3000); awaitthis.sleep(2000); { //升起山峰 consttw=awaitthis.addAnimate({time:0},{time:1},1000,(obj)={ this.material.uniforms.uTime.value=obj.time; TWEEN.remove(tw); //添加标牌 this.addALabel(this.tags[0]); } awaitthis.sleep(2000); //绘制飞线 this.addLine([this.tags[0].pos,this.tags[1].pos],this.tags[0].color); for(leti=1;ithis.tags.length;i++){ constdata=this.tags[i]; //视角跟随到新地点 this.map.setCenter(data.pos,false,1000); this.map.setZoom(6,false,1000); awaitthis.sleep(1000); //绘制山峰 awaitthis.createA(data); //升起山峰 consttw=awaitthis.addAnimate({time:0},{time:1},1000,(obj)={ this.material.uniforms.uTime.value=obj.time; TWEEN.remove(tw); this.addALabel(data); awaitthis.sleep(1000); //添加飞线 if(ithis.tags.length-1){ this.addLine([data.pos,this.tags[i+1].pos],data.color); }else{ //最终视角 this.map.setPitch(73.2,false,3000); this.map.setZoom(5,false,1000); this.map.setRotation(58.7,false,3000); this.map.setCenter([101.6,35.6],false,1000); } } 高德地图3D地图视角设置真的很方便,还自带动画功能,效果真的很棒! setZoom设置缩放大小setPitch设置倾斜角度setCenter设置地图中心经纬度setRotation设置旋转角度8.GitHub地址https://github.com/xiaolidan00/my-earth 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2025-08-09_「转」双汇的“猪”、太太乐的“鸡”,被网友玩成表情包新顶流 下一篇:2025-08-07_古茗CMO顿贤:做品牌是找死,不做是等死

TAG标签:

19
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价