首页 > 解决方案 > 平移、缩放画布以及 D3 中的拖动节点

问题描述

我在 D3 中有以下代码(来自基本示例)用于平移和缩放画布 - 以及用于拖动单个元素的第二个代码块。我想要的是既能够在整个画布上平移/缩放,也能够在用户开始拖动特定对象时移动单个对象。

我拥有的平移/缩放代码是:

var svgCanvas = d3.select('body')
    .append("svg:svg")
    .attr("width", 800)
    .attr("height", 800)

var g = svgCanvas.append("g")

$('body').append(
    $("<div>", {id: "canvas-wrapper" })
        .append("<div/>", { id: "canvas" })
);

var svg = d3.select("svg");
var canvas = d3.select("#canvas");
var canvasWrapper = d3.select("#canvas-wrapper");

var root = svg.append("g");

root.append("rect")
    .attr("x", 50).attr("y", 50)
    .attr("width", 50).attr("height", 50)
    .style("fill", "blue")
    .style("stroke", "green").style("stroke-width", "3px");

root.append("rect")
    .attr("x", 150).attr("y", 150)
    .attr("width", 50).attr("height", 50)
    .style("fill", "blue")
    .style("stroke", "green").style("stroke-width", "3px");

var zoom = d3.zoom()
    .scaleExtent([1, 8])
    .on("zoom", function () {
        var t = d3.event.transform;
        root.attr("transform", t);
        canvas.style("transform", "translateX(" + t.x + "px) translateY(" + t.y + "px) scale(" + t.k + ")")
    });

canvasWrapper.call(zoom);

我的拖动代码是:

var svg = d3.select('body')
    .append("svg:svg")
    .attr("width", 800)
    .attr("height", 800)

var nodes = svg
    .selectAll('.node')
    .data([
        { x: 10, y: 10 }, 
        { x: 100, y: 100 }
    ])
    .enter()
    .append('g')
    .attr('class', 'node')
    .attr("transform", function (data) {
        return "translate(" + [data.x, data.y] + ")";
    })
    .call(function () {
        return d3.drag()
            .on('drag', function (d, i) {
                d.x += d3.event.dx;
                d.y += d3.event.dy;
                d3.select(this).attr("transform", function () {
                    return "translate(" + [d.x, d.y] + ")";
                });
            })
            .on('start', function (d, i) {})
            .on('end', function (d, i) {});
    }());

nodes.append("svg:rect")
    .attr("width", 100)
    .attr("height", 100)
    .style("fill", "red")

我需要做什么才能允许单独拖动每个对象 - 除了在用户拖动打开时拖动整个画布之外?

显然,这两个代码部分之间的区别在于,一个在整个画布上工作,一个在单个元素上工作(第一个有额外的外部容器) - 我如何将两者结合起来?

我试图通过将节点添加到外部容器并将拖动处理添加到每个节点来简单地组合这两个代码示例,但画布似乎捕获了所有事件,因此忽略了节点上的拖动。

组合的非工作代码:

d3.select('body')
    .append("svg:svg")
    .attr("width", 800)
    .attr("height", 800);

$('body').append(
    $("<div>", {
        id: "canvas-wrapper"
    }).append("<div/>", {
        id: "canvas"
    })
);

var svg = d3.select("svg");
var canvas = d3.select("#canvas");
var canvasWrapper = d3.select("#canvas-wrapper");

var root = svg.append("g");

var nodes = root
    .selectAll('.node')
    .data(dataArray)
    .enter()
    .append('g')
    .attr('class', 'node')
    .attr("transform", function (data) {
        return "translate(" + [data.x, data.y] + ")";
    })
    .call(function () {
        return d3.drag().on('drag', function (d, i) {
            d.x += d3.event.dx;
            d.y += d3.event.dy;
            d3.select(this).attr("transform", function () {
                return "translate(" + [d.x, d.y] + ")";
            });
        })
    }());

nodes.append("svg:rect")
    .attr("width", 50)
    .attr("height", 50)
    .style("fill", "#ffb5b5")
    .style("fill", "#ffb5b5")
    .style("stroke", "green").style("stroke-width", "3px");

var zoom = d3.zoom()
    .scaleExtent([1, 8])
    .on("zoom", function () {
        var t = d3.event.transform;
        root.attr("transform", t);
        canvas.style("transform", "translateX(" + t.x + "px) translateY(" + t.y + "px) scale(" + t.k + ")")
    });

canvasWrapper.call(zoom);

解决了:

通过检测画布中的鼠标事件位置,可以隐藏/显示覆盖 SVG 画布的画布。因此,当有在节点上启动的鼠标事件时,应隐藏画布以允许拖动功能,而其他事件应被画布捕获以允许平移/缩放

标签: d3.js

解决方案


作为第一件事,添加 arectsvg在 this 上调用 zoom rect。任何未被元素捕获的鼠标事件都将用于缩放,移除画布包装。

对于 Chrome,您可以在 上调用 zoom svg,无需额外的rect.

var dataArray = [{x:100, y:100}, {x:200, y:200}];

var svgWidth = 800, svgHeight = 800;

var svg = d3.select('body')
    .append("svg:svg")
    .attr("width", svgWidth)
    .attr("height", svgHeight);
var zoomRect = svg.append('rect')
    .attr("width", svgWidth)
    .attr("height", svgHeight)
    .attr("fill", "white");

var root = svg.append("g");

var nodes = root
    .selectAll('.node')
    .data(dataArray)
    .enter()
    .append('g')
    .attr('class', 'node')
    .attr("transform", function (data) {
        return "translate(" + [data.x, data.y] + ")";
    })
    .call(function () {
        return d3.drag().on('drag', function (d, i) {
            d.x += d3.event.dx;
            d.y += d3.event.dy;
            d3.select(this).attr("transform", function () {
                return "translate(" + [d.x, d.y] + ")";
            });
        })
    }());

nodes.append("svg:rect")
    .attr("width", 50)
    .attr("height", 50)
    .style("fill", "#ffb5b5")
    .style("stroke", "green").style("stroke-width", "3px");

var zoom = d3.zoom()
    .scaleExtent([1, 8])
    .on("zoom", function () {
        var t = d3.event.transform;
        root.attr("transform", t);
        //canvas.style("transform", "translateX(" + t.x + "px) translateY(" + t.y + "px) scale(" + t.k + ")");
    });

zoomRect.call(zoom);
<script src="http://d3js.org/d3.v5.min.js"></script>


推荐阅读