Реализовать панорамирование, сохраняя при этом перетаскивание узлов в силовом макете d3.

Я использую d3.layout.force для визуализации структур графа. И мне нужна как функция перетаскивания узла, так и функция панорамирования. (Под «панорамированием» я имею в виду что-то вроде поведения на картах Google.)

Как показано в нескольких примерах d3, узлы можно сделать перетаскиваемыми с помощью nodes.call(force.drag). А панорамирование можно реализовать с помощью d3.behavior.drag.

Однако, похоже, их нельзя использовать одновременно.

Вот пример кода:

var width = 400, height = 300;
var svg = d3.select('body').append('svg').attr({width: width, height: height});

var nodes = [{}, {}, {}, {}];
var links = [
    {source: 1, target: 0},
    {source: 2, target: 0},
    {source: 0, target: 3}
];

var nodeSel = svg.selectAll('circle')
    .data(nodes).enter().append('circle')
    .attr('r', 10).attr('fill', 'black');
var linkSel = svg.selectAll('line').data(links).enter().append('line')
    .attr('stroke', 'black');

var force = d3.layout.force()
    .size([width, height])
    .nodes(nodes).links(links)
    .linkDistance(80)
    .charge(-300)
    .on('tick', function() {
        linkSel
            .attr('x1', function(d) { return d.source.x; })
            .attr('y1', function(d) { return d.source.y; })
            .attr('x2', function(d) { return d.target.x; })
            .attr('y2', function(d) { return d.target.y; });
        nodeSel
            .attr('cx', function(d) { return d.x; })
            .attr('cy', function(d) { return d.y; });
    });

nodeSel.call(force.drag);


/* This block of codes spoils force.drag */
/* ****************************************
var drag = d3.behavior.drag();
var viewBoxX = 0, viewBoxY = 0;
drag.on('dragstart', function() {
    console.log('new dragstart is called');
}).on('drag', function() {
    viewBoxX -= d3.event.dx;
    viewBoxY -= d3.event.dy;
    svg.attr('viewBox', [viewBoxX, viewBoxY, width, height].join(' '));
}).on('dragend', function() {
    console.log('new dragend is called');
});
svg.call(drag);
**************************************** */


force.start();

(Рабочий находится на jsfiddle: http://jsfiddle.net/dersinces/hzL8T/1/.)

Этот код разрешает только перетаскивание узлов, но не панорамирование. Активация блока кода внизу (строка 36) включает панорамирование, но делает узлы недоступными для перетаскивания.

Как я могу реализовать панорамирование графика, сохраняя при этом узлы перетаскиваемыми?


person dersinces    schedule 20.11.2013    source источник
comment
Вам нужно только поведение масштабирования (оно также выполняет перетаскивание). См., например. здесь.   -  person Lars Kotthoff    schedule 20.11.2013
comment
Спасибо, @LarsKotthoff! Я посмотрю на d3.behavior.zoom.   -  person dersinces    schedule 21.11.2013


Ответы (1)


Вот как это можно сделать: Демо

Идея состоит в том, чтобы поместить область с перетаскиваемым поведением под область, содержащую узлы, чтобы узлы по-прежнему могли получать события мыши.

// First append the area where the dragging will happen.
svg.append('rect')
  .classed('bg', true)
  .attr('stroke', 'transparent')
  .attr('fill', 'transparent')
  .attr('x', 0)
  .attr('y', 0)
  .attr('width', width)
  .attr('height', height)
  .call(drag);


// Then add the area which will contain the nodes.
var nodeArea = svg.append('g')
                 .classed('node-area', true);
person musically_ut    schedule 20.11.2013
comment
Демонстрация, похоже, не решает мою проблему. Узлы нельзя перетаскивать, а панорамирование не работает для ссылок. Возможно, вы хотите вставить невидимый прямоугольник (та же идея с примером @LarsKotthoff). Но таким образом панорамирование ограничено размером прямоугольника. Я хотел бы другое решение, если это возможно. - person dersinces; 21.11.2013
comment
Гах! Извини за это. Я дал ссылку на более старую демо-версию. Версия, на которую я сейчас указал, должна работать. - person musically_ut; 22.11.2013
comment
Извините за опоздание с ответом, @musically_ut. Ваша новая демонстрация работает отлично! Вы решили мою проблему. Большое спасибо! - person dersinces; 23.11.2013