0

I have implemented a network graph using canvas using this example (Force Layout with Canvas) with functionality to drag (Circle Dragging II) and to zoom (Canvas geometric zooming) all updated to V4.

The problem is that when I zoom or drag the canvas and applying:

canvas.translate(d3.event.translate[0], d3.event.translate[1]);
canvas.scale(d3.event.scale, d3.event.scale);

The nodes objects are somehow not updated accordingly. They are drawn just fine before the canvas.restore();, but when I try to target a node with the mouse using:

function getDragSubject() {
    for (var i = nodes.length - 1, node, x, y; i >= 0; --i) {
        node = nodes[i];
        x = node.x - d3.event.x;
        y = node.y - d3.event.y;

        if (x * x + y * y < radius * radius) return node;
    }
}

Nothing happens.

I end up doing this:

function getDragSubject() {
    for (var i = nodes.length - 1, node, x, y; i >= 0; --i) {
        node = nodes[i];
        x = node.x * scale + translation[0] - d3.event.x;
        y = node.y * scale + translation[1] - d3.event.y;

        if (x * x + y * y < (radius * scale) * (radius * scale)) return node;
    }
}

Saving and adding the translations and scale so that I can properly select them after canvas drag/zoom, but it doesn't seems to be the right way to me. Also when I am redrawing the canvas after dragging I am having issues with the nodes properly positioning.

function draw() {
    // draw links
    context.strokeStyle = '#ccc';
    context.beginPath();
    links.forEach(function(d) {
        context.moveTo(d.source.x * scale + translation[0], d.source.y * scale + translation[1]);
        context.lineTo(d.target.x * scale + translation[0], d.target.y * scale + translation[1]);
    });
    context.stroke();

    // draw nodes
    context.fillStyle = 'steelblue';
    context.beginPath();
    nodes.forEach(function(d) {
        context.moveTo(d.x * scale + translation[0], d.y * scale + translation[1]);
        context.arc(d.x * scale + translation[0], d.y * scale + translation[1], radius * scale, 0, 2 * Math.PI);
    });
    context.fill();
}

Anyway this doesn't look right to me.

Anyone with suggestions about improving or modifying?

Hristo Enev
  • 2,421
  • 18
  • 29

1 Answers1

1

So I guess the solution could be found in this post: Mouse coordinates don't match after Scaling and Panning canvas. Draw function shouldn't be updated - stays the same as before.

function draw() {
    // draw links
    context.strokeStyle = '#ccc';
    context.beginPath();
    links.forEach(function(d) {
        context.moveTo(d.source.x, d.source.y);
        context.lineTo(d.target.x, d.target.y);
    });
    context.stroke();

    // draw nodes
    context.fillStyle = 'steelblue';
    context.beginPath();
    nodes.forEach(function(d) {
        context.moveTo(d.x, d.y);
        context.arc(d.x, d.y, radius, 0, 2 * Math.PI);
    });
    context.fill();
}

Dragging handler function should be updated:

var rect = canvas.getBoundingClientRect();

function onDrag() {
    d3.event.subject.fx = ((d3.event.sourceEvent.pageX - rect.left) - translation[0]) / scale;
    d3.event.subject.fy = ((d3.event.sourceEvent.pageY - rect.top) - translation[1]) / scale;
}
Community
  • 1
  • 1
Hristo Enev
  • 2,421
  • 18
  • 29