在D3中平移,缩放画布以及拖动节点

时间:2018-11-21 20:59:08

标签: d3.js

我在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画布的画布。因此,当在某个节点上启动了鼠标事件时,应隐藏画布以允许拖动以起作用,而其他事件应被画布捕获以允许平移/缩放

1 个答案:

答案 0 :(得分:0)

首先,将rect添加到svg并在此rect上调用zoom。未被元素捕获的任何鼠标事件都将用于缩放,请删除画布包装器。

对于Chrome,您可以在svg上调用zoom,而无需其他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>