在D3.js中为多边形添加删除图标并跟踪剩余多边形的技术问题
D3.js多边形添加删除图标实现方案
核心问题解答
1. 如何将删除图标添加到多边形顶部边缘
要在多边形顶部放置图标,关键是定位多边形的顶部顶点(SVG坐标系中Y值最小的点,因为Y轴向下延伸):
- 解析多边形的
points字符串,转换为[x,y]格式的坐标数组 - 遍历坐标数组,筛选出Y值最小的点(若存在多个相同最小Y值的点,取X坐标居中的点更美观)
- 在该点位置(可适当向上偏移,避免与多边形边缘重叠)添加删除图标
2. 删除图标选择:SVG图形优于PNG
优先使用原生SVG图形实现删除图标,优势如下:
- 矢量图形缩放不失真,适配不同分辨率场景
- 无需依赖外部图片资源,加载速度更快
- 可通过D3动态修改样式(颜色、大小等)
常见实现方式:用圆形做背景,叠加两条直线组成叉号,或直接用<path>绘制删除符号
3. 是否需要计算图形边界?
不需要计算完整的多边形边界包围盒,仅需找到顶部顶点即可。如果后续有其他边界相关需求,可使用SVG元素的getBBox()方法获取包围盒信息,但当前需求下只需提取顶部坐标。
完整实现代码
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script> $(document).ready(function() { var vis = d3.select("body").append("svg") .attr("width", 1000) .attr("height", 667); // 跟踪剩余多边形数量 var polygonCount = 0; var data = [{ "code": "BR23", "points": "405,100.5625,331,108.5625,300,120.5625,290,141.5625,283,158.5625,278,171.5625,289,209.5625,304,231.5625,332,255.5625,351,264.5625,383,268.5625,400,266.5625,440,266.5625,462,266.5625,510,257.5625,522,230.5625,530,210.5625,534,183.5625,524,163.5625,512,148.5625,490,134.5625,462,124.5625" }, { "code": "FR23", "points": "702,164.5625,594,250.5625,689,324.5625,749,261.5625,770,206.5625" } ]; // 预处理数据:解析points为坐标数组,并找到顶部点 data.forEach(function(d) { var pointStrs = d.points.split(','); d.coords = []; for(var i=0; i<pointStrs.length; i+=2) { d.coords.push([parseFloat(pointStrs[i]), parseFloat(pointStrs[i+1])]); } // 筛选Y值最小的顶部点 d.topPoint = d.coords.reduce(function(prev, curr) { return curr[1] < prev[1] ? curr : prev; }, d.coords[0]); }); // 创建多边形组:将多边形和删除图标绑定到同一组,方便批量移除 var polygonGroups = vis.selectAll(".polygon-group") .data(data) .enter().append("g") .attr("class", "polygon-group"); // 添加多边形元素 polygonGroups.append("polygon") .attr("points", function(d) { return d.points; }) .attr("fill", "transparent") .attr("stroke", "red") .attr("stroke-width", 2) .style("stroke-dasharray", "15, 15") .on("click", function(d) { d3.select(this).style("fill", "blue"); }); // 定义删除图标尺寸 var deleteIconSize = 20; // 添加删除图标背景圆 polygonGroups.append("circle") .attr("cx", function(d) { return d.topPoint[0]; }) .attr("cy", function(d) { return d.topPoint[1] - deleteIconSize/2; }) // 向上偏移避免与多边形重叠 .attr("r", deleteIconSize/2) .attr("stroke", "#333") .attr("fill", "#ff4444") .style("cursor", "pointer") .on("click", function() { // 移除当前多边形组 d3.select(this.parentNode).remove(); // 更新并输出剩余数量 polygonCount = vis.selectAll(".polygon-group").size(); console.log("剩余多边形数量:", polygonCount); }); // 添加叉号线条(第一条) polygonGroups.append("line") .attr("x1", function(d) { return d.topPoint[0] - deleteIconSize/3; }) .attr("y1", function(d) { return d.topPoint[1] - deleteIconSize/2 + deleteIconSize/3; }) .attr("x2", function(d) { return d.topPoint[0] + deleteIconSize/3; }) .attr("y2", function(d) { return d.topPoint[1] - deleteIconSize/2 - deleteIconSize/3; }) .attr("stroke", "#fff") .attr("stroke-width", 2) .style("cursor", "pointer") .on("click", function() { d3.select(this.parentNode).remove(); polygonCount = vis.selectAll(".polygon-group").size(); console.log("剩余多边形数量:", polygonCount); }); // 添加叉号线条(第二条) polygonGroups.append("line") .attr("x1", function(d) { return d.topPoint[0] - deleteIconSize/3; }) .attr("y1", function(d) { return d.topPoint[1] - deleteIconSize/2 - deleteIconSize/3; }) .attr("x2", function(d) { return d.topPoint[0] + deleteIconSize/3; }) .attr("y2", function(d) { return d.topPoint[1] - deleteIconSize/2 + deleteIconSize/3; }) .attr("stroke", "#fff") .attr("stroke-width", 2) .style("cursor", "pointer") .on("click", function() { d3.select(this.parentNode).remove(); polygonCount = vis.selectAll(".polygon-group").size(); console.log("剩余多边形数量:", polygonCount); }); // 初始化数量 polygonCount = data.length; console.log("初始多边形数量:", polygonCount); }); </script>
关键实现细节
- 使用
<g>元素将多边形与删除图标分组,实现点击图标时批量移除关联元素 - 预处理阶段解析坐标数组并计算顶部点,避免重复计算提升性能
- 删除图标由圆形背景和两条直线组成,点击图标任意区域均可触发删除操作
- 通过
selectAll(".polygon-group").size()实时获取剩余多边形数量
内容的提问来源于stack exchange,提问作者The Old County
相关产品推荐
相关产品推荐

