求助:解决D3.js交互式散点图拖拽时数据点无法跟随光标问题
解决D3散点图拖拽时位置偏移的问题
问题根源
拖拽时坐标计算错误:D3 v6的拖拽事件event.x/event.y是基于整个SVG画布的绝对坐标,而你的图表元素被包裹在经过translate(${margin.left},${margin.top})平移的<g>容器中,直接使用原始坐标会导致y轴映射完全错误,表现为点跳到图表底部且只能沿x轴移动。
修复方案
在dragged函数中,先将拖拽事件的绝对坐标转换为<g>容器内的相对坐标,再进行比例尺反转计算:
function dragged(event, d) { // 转换为容器内的相对坐标 const containerX = event.x - margin.left; const containerY = event.y - margin.top; // 反转比例尺获取数据值 const newX = x.invert(containerX); const newY = y.invert(containerY); // 限制数据范围在定义域内 d.x = Math.max(24, Math.min(48, newX)); d.y = Math.max(10000, Math.min(100000, newY)); // 更新点的位置 d3.select(this) .attr("cx", x(d.x)) .attr("cy", y(d.y)); }
完整修复后的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interactive D3 Chart</title> <script src="https://d3js.org/d3.v6.min.js"></script> <style> .axis path, .axis line { fill: none; shape-rendering: crispEdges; } .axis text { font-size: 12px; } .dot { fill: steelblue; stroke: #000; stroke-width: 1.5px; } .dragging { fill: orange; } </style> </head> <body> <script> // Set dimensions and margins of the graph const margin = { top: 20, right: 30, bottom: 40, left: 50 }, width = 800 - margin.left - margin.right, height = 600 - margin.top - margin.bottom; // Append the svg object to the body of the page const svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); // X scale and axis const x = d3.scaleLinear() .domain([24, 48]) .range([0, width]); svg.append("g") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x)); // Y scale and axis const y = d3.scaleLinear() .domain([10000, 100000]) .range([height, 0]); svg.append("g") .call(d3.axisLeft(y)); // Initial data points const data = [ {x: 30, y: 20000}, {x: 35, y: 50000}, {x: 40, y: 75000}, ]; // Create and place the "dots" const dots = svg.selectAll(".dot") .data(data) .enter().append("circle") .attr("class", "dot") .attr("cx", d => x(d.x)) .attr("cy", d => y(d.y)) .attr("r", 5) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended) ); function dragstarted(event, d) { d3.select(this).raise().classed("dragging", true); } function dragged(event, d) { // 转换为容器内的相对坐标 const containerX = event.x - margin.left; const containerY = event.y - margin.top; const newX = x.invert(containerX); const newY = y.invert(containerY); d.x = Math.max(24, Math.min(48, newX)); d.y = Math.max(10000, Math.min(100000, newY)); d3.select(this) .attr("cx", x(d.x)) .attr("cy", y(d.y)); } function dragended(event, d) { d3.select(this).classed("dragging", false); } </script> </body> </html>
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

