how to add mouse events to force directed graph using the d3 canvas renderer?

試著忘記壹切 提交于 2019-12-11 04:52:33

问题


All the other examples have mouse events with svg.append().... I don't know where to "enter" to get the arcs in a canvas renderer and add .on('click', function(){}) in v4 style. I want to click to get the value of d. Where do I add the handler in this example? I understand the old way below this example.

Might something like this work? d3.select(canvas).call(d3.mouse()).on("click", ...)

link to working example

var links = d3.range(nodes.length - 1).map(function(i) {
  return {
    source: Math.floor(Math.sqrt(i)),
    target: i + 1
  };
});

var simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody())
    .force("link", d3.forceLink(links).distance(20).strength(1))
    .force("x", d3.forceX())
    .force("y", d3.forceY())
    .on("tick", ticked);

var canvas = document.querySelector("canvas"),
    context = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height;

d3.select(canvas)
    .call(d3.drag()
        .container(canvas)
        .subject(dragsubject)
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

function ticked() {
  context.clearRect(0, 0, width, height);
  context.save();
  context.translate(width / 2, height / 2);

  context.beginPath();
  links.forEach(drawLink);
  context.strokeStyle = "#aaa";
  context.stroke();

  context.beginPath();
  nodes.forEach(drawNode);
  context.fill();
  context.strokeStyle = "#fff";
  context.stroke();

  context.restore();
}

function dragsubject() {
  return simulation.find(d3.event.x - width / 2, d3.event.y - height / 2);
}

function dragstarted() {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d3.event.subject.fx = d3.event.subject.x;
  d3.event.subject.fy = d3.event.subject.y;
}

function dragged() {
  d3.event.subject.fx = d3.event.x;
  d3.event.subject.fy = d3.event.y;
}

function dragended() {
  if (!d3.event.active) simulation.alphaTarget(0);
  d3.event.subject.fx = null;
  d3.event.subject.fy = null;
}

function drawLink(d) {
  context.moveTo(d.source.x, d.source.y);
  context.lineTo(d.target.x, d.target.y);
}

function drawNode(d) {
  context.moveTo(d.x + 3, d.y);
  context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}

old way

var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
  .attr("r", 8)
  .attr("fill", function(d) { return color(d.group); })
  .on("click", togglenode)
  .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));

回答1:


In D3 v4.x, you add a click event pretty much the same way you do in v3.x:

selection.on("click", function(d){
    //do whatever you want with the datum
});

The problem in your question is not v3 versus v4, that's not the issue in the code you shared. The problem with that code is that it uses HTML canvas, not SVG, to render the dataviz.

Unlike SVG, canvas doesn't have a node tree of elements. You cannot "select something" and add an event handler to it.

Think of canvas as a raster image, like a BMP or a JPEG. You can find on what x and y position you clicked, you can even find the colour of that pixel, but you cannot select a given node element, because canvas has none.

For instance, check this tutorial from Nadieh Bremer to see how complicated is to get the circle the user clicks on when you use HTML canvas.




回答2:


Since I used the canvas renderer, I just cheated and used the d3 dragstart event already in the example. There's probably a way to do it like this I'd like to know.

        d3.select(canvas)
        .call(d3.drag()
            .container(canvas)
            .subject(dragsubject)
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

function dragstarted(d) {
            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
            d3.event.subject.fx = d3.event.subject.x;
            d3.event.subject.fy = d3.event.subject.y;
            //broadcast the selection to parent
            emitter.emit( d3.event.subject );
        }



回答3:


Expanded FlavorScape's hack with double click

        d3.select(canvas)
        .call(d3.drag()
            .container(canvas)
            .subject(dragsubject)
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));
        var clickDate = new Date();
        var difference_ms;
        function dragstarted(d) {
            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
            d3.event.subject.fx = d3.event.subject.x;
            d3.event.subject.fy = d3.event.subject.y;
            difference_ms = (new Date()).getTime() - clickDate.getTime();
            clickDate = new Date();
            //if clicks less than 200ms apart (double click)
            if(difference_ms < 200)
                console.log( d3.event.subject );
        }


来源:https://stackoverflow.com/questions/40988437/how-to-add-mouse-events-to-force-directed-graph-using-the-d3-canvas-renderer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!