news 2026/10/29 2:02:11

Openlayers地图管道中添加双向燕尾水流动画效果

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Openlayers地图管道中添加双向燕尾水流动画效果

1:需求是这样的,需要在openlayers地图加载的管道上添加双向燕尾水流动画效果如下

openlayers

代码如下:

// 启动水流动画(蓝底带箭尾效果)startFlowAnimation(){if(this.flowPathFeatures.length===0)return;// 清理之前的动画if(this.flowAnimationFrame){cancelAnimationFrame(this.flowAnimationFrame);this.flowAnimationFrame=null;}if(this.flowAnimationInterval){clearInterval(this.flowAnimationInterval);this.flowAnimationInterval=null;}// 获取第一个管道特征的起始偏移量constfirstFeature=this.flowPathFeatures[0];conststartOffset=firstFeature?firstFeature.get("startOffset")||0:0;this.flowPx=0;// 优化:使用固定时间间隔更新,降低CPU使用率constanimate=()=>{// 每帧移动1.2像素,形成可见的水流流动效果(约14px/s)this.flowPx=this.flowPx+1.2;constallFlowFeatures=[];// 为每条管道路径创建蓝底带箭尾的流动效果this.flowPathFeatures.forEach((feature)=>{constcoords=feature.get("flowCoords");consttotalLength=feature.get("totalLength");constsegments=feature.get("segments");if(!coords||coords.length<2||totalLength<=0)return;constisBranch=feature.get("isBranch");constpipeColor=isBranch?this.pipeColors.branchPipe:this.pipeColors.mainPipe;constpipeColorRgba=pipeColor.replace(")",", 0.8)").replace("rgb","rgba");constpipeColorLight=isBranch?this.pipeColors.branchPipe:this.pipeColors.mainPipe;// 创建底线条(等宽)constbaseLineFeature=newFeature({geometry:newLineString(coords),});baseLineFeature.setStyle([// 底色(等宽)newStyle({stroke:newStroke({color:pipeColorRgba,width:isBranch?3:4,// 保持等宽lineCap:"round",lineJoin:"round",}),}),// 边框newStyle({stroke:newStroke({color:pipeColorLight,width:isBranch?3:4,lineCap:"round",lineJoin:"round",}),}),]);allFlowFeatures.push(baseLineFeature);// 获取该管道的起始偏移量constfeatureStartOffset=feature.get("startOffset")||0;constisYibiaoduan=feature.get("isYibiaoduan");constflowStartOffset=isYibiaoduan?0:featureStartOffset;// 使用自定义渲染器沿管道连续绘制交替颜色的燕尾箭头(头尾相连无空隙)constpipeFeature=newFeature({geometry:newLineString(coords),});// 燕尾箭头尺寸参数(像素单位)constheadLen=isBranch?12:12;// 箭头总长度(一个完整周期,也是步进间距)constbodyLen=isBranch?8:10;// 矩形主体长度consttipLen=isBranch?12:14;// 箭头尖端位置(= headLen)consthalfWidth=isBranch?3:3;// 半宽度constnotchDepth=isBranch?3:3;// 尾部V型缺口深度pipeFeature.setStyle(newStyle({renderer:(pixelCoordinates,state)=>{constctx=state.context;constpixelCoords=pixelCoordinates;if(!pixelCoords||pixelCoords.length<2)return;// 计算每个像素段的长度、方向和累积距离constsegs=[];constcumDist=[0];lettotalLen=0;for(leti=0;i<pixelCoords.length-1;i++){constdx=pixelCoords[i+1][0]-pixelCoords[i][0];constdy=pixelCoords[i+1][1]-pixelCoords[i][1];constlen=Math.sqrt(dx*dx+dy*dy);segs.push({x1:pixelCoords[i][0],y1:pixelCoords[i][1],x2:pixelCoords[i+1][0],y2:pixelCoords[i+1][1],len,dx,dy,});totalLen+=len;cumDist.push(totalLen);}if(totalLen<=0)return;// 流动偏移量(像素),箭头沿管道方向从起点向终点移动constflowShift=(((flowStartOffset*headLen+this.flowPx)%headLen)+headLen)%headLen;// 沿管道按箭头长度步进,绘制离散燕尾箭头(头尾相接)letpos=0;letarrowIdx=0;while(pos<totalLen){constdrawPos=pos+flowShift;if(drawPos>=totalLen)break;// 找到该位置所在的线段letsi=0;for(lets=0;s<segs.length;s++){if(cumDist[s+1]>=drawPos){si=s;break;}}constseg=segs[si];constt=seg.len>0?(drawPos-cumDist[si])/seg.len:0;constcx=seg.x1+seg.dx*t;constcy=seg.y1+seg.dy*t;// 方向角度(管道走向)constangle=Math.atan2(seg.dy,seg.dx);// 箭头必须完全在当前线段内,否则跳过(防止在管道转角处跑出管道)constarrowHalfLen=tipLen/2;constdistToSegEnd=cumDist[si+1]-drawPos;if(distToSegEnd<headLen/2||drawPos<headLen/2){pos+=headLen;arrowIdx++;continue;}// 交替颜色:使用管道主色和白色constisMainColor=arrowIdx%2===0;// 将管道颜色从 hex 转换为 rgbconsthexToRgb=(hex)=>{constresult=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);returnresult?{r:parseInt(result[1],16),g:parseInt(result[2],16),b:parseInt(result[3],16)}:{r:100,g:200,b:255};};constmainRgb=hexToRgb(pipeColor);constr=isMainColor?mainRgb.r:255;constg=isMainColor?mainRgb.g:255;constb=isMainColor?mainRgb.b:255;// 绘制燕尾箭头(6个顶点,tip指向管道方向)// 形状:[tip]──[body]──[notch]// tip尖端在前方,notch V型缺口在后方ctx.save();ctx.translate(cx,cy);// 角度基于像素坐标计算(Y轴向下),直接旋转即可对齐管道方向ctx.rotate(angle);ctx.beginPath();ctx.moveTo(tipLen,0);// 箭头尖端(最前方)ctx.lineTo(bodyLen,-halfWidth);// 右上外角ctx.lineTo(0,-halfWidth);// 左上角(尾部上沿)ctx.lineTo(notchDepth,0);// V型缺口尖端(向内凹陷)ctx.lineTo(0,halfWidth);// 左下角(尾部下沿)ctx.lineTo(bodyLen,halfWidth);// 右下外角ctx.closePath();ctx.fillStyle=`rgba(${r},${g},${b}, 0.9)`;ctx.fill();// 浅蓝色边框ctx.strokeStyle="rgba(173, 216, 230, 0.8)";ctx.lineWidth=1;ctx.stroke();ctx.restore();pos+=headLen;arrowIdx++;}},}));allFlowFeatures.push(pipeFeature);});// 更新水流动画图层this.flowAnimationLayer.getSource().clear();this.flowAnimationLayer.getSource().addFeatures(allFlowFeatures);};// 使用setInterval控制帧率(约12fps)this.flowAnimationInterval=setInterval(animate,85);},
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 2:00:54

实时湖仓与区块链:数据架构的信任革命

1. 引言 在大数据技术演进的浪潮中&#xff0c;实时湖仓&#xff08;Real-Time Lakehouse&#xff09;与区块链&#xff08;Blockchain&#xff09;看似分属两个截然不同的技术领域——前者追求数据的流动性与分析效率&#xff0c;后者强调数据的不可篡改与信任机制。然而&…

作者头像 李华
网站建设 2026/10/29 2:00:52

《派出你的AI同事:WorkBuddy案例实战》085:IM 与邮件新连接器实战

《派出你的AI同事:WorkBuddy案例实战》085:IM 与邮件新连接器实战 本文是《唤醒你的 AI 同事:WorkBuddy 全栈指南》系列 卷三(案例实战) 的第 85 篇。前面讲了国内办公平台(企业微信/飞书/钉钉);这一篇补齐国际化 IM 与邮件方向的新连接器。 ⚠️ 版本说明:本篇依据**…

作者头像 李华
网站建设 2026/10/29 1:57:42

老旧 Mac 如何装最新 macOS:OpenCore Legacy Patcher 完整实战指南

老旧 Mac 如何装最新 macOS&#xff1a;OpenCore Legacy Patcher 完整实战指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher 是让…

作者头像 李华
网站建设 2026/10/29 1:57:10

Python后端部署排障06:curl 没报错,为什么业务请求还是失败了

Python后端部署排障06&#xff1a;curl 没报错&#xff0c;为什么业务请求还是失败了 网络层已经连通&#xff0c;curl 也输出了一段 JSON&#xff0c;于是你在群里说“恢复了”。几分钟后用户仍无法确认报价&#xff1a;原来 JSON 是一个版本冲突错误&#xff0c;HTTP 状态为 …

作者头像 李华