You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 05:13:16