OpenLayers6 如何绘制可编辑的带自定义半圆描边的云朵图形
OpenLayers 6 可编辑云朵图形实现方案
核心思路是分离编辑控制点和渲染轮廓:不要把组成云朵圆弧的所有顶点都存到要素几何里,否则编辑时会出现上百个可拖拽节点,根本无法操作。只需要存储少量核心控制点,云朵的半圆蓬松轮廓在渲染阶段动态生成,所有编辑操作只针对核心控制点,交互流畅度和普通折线/多边形编辑完全一致。
具体实现步骤
1. 核心轮廓渲染逻辑
要素本身的几何只存储10个以内的核心控制点(用ol/geom/LineString或者简单Polygon都可以),云朵的真实轮廓通过自定义样式渲染动态生成:
- 遍历相邻的两个核心控制点,以两点连线为直径,向路径外侧生成半圆弧采样点(单段圆弧采样16个点足够平滑)
- 把所有段的半圆弧点拼接成闭合路径,就是完整的云朵轮廓
- 可以给每段圆弧的半径加±20%以内的随机偏移,模拟自然云朵的不规则蓬松感,不要所有圆弧大小完全一致
核心实现代码参考:
/** * 生成两点之间外凸半圆的采样点 * @param {number[]} p1 起点坐标 * @param {number[]} p2 终点坐标 * @param {number} sampleCount 圆弧采样点数 * @param {number} radiusScale 半径缩放系数,用来调整圆弧凸起程度 * @returns {number[][]} 半圆弧采样点序列 */ function getArcPoints(p1, p2, sampleCount = 16, radiusScale = 1) { const mid = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2]; const baseRadius = Math.hypot(p2[0] - p1[0], p2[1] - p1[1]) / 2; const radius = baseRadius * radiusScale; // 计算两点连线的垂直外法线方向,作为半圆的凸起方向 const lineAngle = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]); const arcStartAngle = lineAngle - Math.PI / 2; const points = []; // 从p1到p2扫过180度生成采样点 for (let i = 0; i <= sampleCount; i++) { const currentAngle = arcStartAngle + Math.PI * (i / sampleCount); points.push([ mid[0] + radius * Math.cos(currentAngle), mid[1] + radius * Math.sin(currentAngle) ]); } return points; } // 云朵要素样式,直接用canvas自定义渲染性能最优 const cloudStyle = new ol.style.Style({ renderer: (coords, state) => { const corePoints = coords[0]; const ctx = state.context; ctx.beginPath(); let isFirst = true; // 拼接所有段的半圆弧 for (let i = 0; i < corePoints.length - 1; i++) { // 拖拽编辑时固定半径为1避免轮廓抖动,静态展示时可加随机偏移 const rScale = state.dragging ? 1 : 0.9 + Math.random() * 0.2; const arcPoints = getArcPoints(corePoints[i], corePoints[i+1], 16, rScale); arcPoints.forEach(p => { if (isFirst) { ctx.moveTo(p[0], p[1]); isFirst = false; } else { ctx.lineTo(p[0], p[1]); } }); } ctx.closePath(); // 自定义填充和描边样式 ctx.fillStyle = 'rgba(255,255,255,0.9)'; ctx.fill(); ctx.strokeStyle = '#2d3748'; ctx.lineWidth = 2; ctx.stroke(); } });
2. 编辑交互配置
因为要素几何里只存了核心控制点,直接复用OpenLayers原生的编辑交互就能实现所有要求的操作,不需要自己写复杂的拖拽逻辑:
- 节点拖拽/形态拉伸:直接给矢量图层绑定
ol/interaction/Modify交互,拖拽核心点时,样式会实时重绘云朵轮廓,延迟极低 - 新增节点:监听地图
click事件,按住Ctrl键点击两个核心控制点之间的线段时,把点击位置的坐标插入到原始几何坐标序列的对应索引位置即可 - 删除节点:监听键盘
Delete键事件,选中节点后直接从坐标序列中移除对应点即可,注意最少保留3个核心点避免图形失效 - 整体缩放/平移:配合
ol/interaction/Translate实现整体平移,给选中要素加四个边角的缩放手柄,拖拽手柄时对所有核心控制点做统一仿射变换即可,不需要修改渲染逻辑
3. 效果调优注意事项
- 初始绘制云朵时,只需要点6-8个核心点围成一个不规则的闭合圈即可,渲染后自动生成蓬松的云朵效果,不需要刻意画圆弧
- 如果需要更精细的形态调整,可以给每段圆弧单独存半径缩放系数,选中线段时弹出滑块调整对应段的凸起程度
- 不要把单段圆弧的采样点设到32个以上,会明显降低拖拽时的渲染帧率,16个点的平滑度已经完全满足展示需求

避坑提示:不要在渲染函数里频繁创建新的
ol/geom/Polygon等OpenLayers对象,直接用canvas上下文绘制性能是最好的,大比例尺下拖拽也不会卡。如果需要实现多层云朵叠加的效果,只要给不同层的云朵设置不同的填充透明度和偏移量即可。
内容的提问来源于stack exchange,提问作者BinaryHawk
相关产品推荐
相关产品推荐

