如何在javascript中的d3.js中拖动节点?

问题描述

以下是使用d3.js制作力向图的代码

如何仅在拖动节点时使节点拖动?

当前的原因是,当您在舞台上的任何位置拖动时,附近的某些节点将被拖动。相反,如何约束拖动仅在拖动真实节点(而不是周围区域)时发生

我又如何使舞台可缩放和拖动?

const graph = {
  nodes: [{
      name: 'john',age: 35
    },{
      name: 'simon',age: 37
    },{
      name: 'manjoor',{
      name: 'lorg',age: 34
    },{
      name: 'kilvin',age: 32
    },],links: [{
      source: 'john',target: 'simon'
    },{
      source: 'john',target: 'manjoor'
    },{
      source: 'simon',target: 'lorg'
    },target: 'kilvin'
    },{
      source: 'manjoor',{
      source: 'lorg',target: 'john'
    },{
      source: 'kilvin',]
}


const canvas = d3.select('#network')

const width = canvas.attr('width')
const height = canvas.attr('height')
const r = 30
const ctx = canvas.node().getContext('2d')

const color = d3.scaleOrdinal(d3.schemeAccent);

const simulation = d3.forceSimulation()
  .force('x',d3.forceX(width / 2))
  .force('y',d3.forceY(height / 2))
  .force('collide',d3.forceCollide(r + 20))
  .force('charge',d3.forceManyBody().strength(-100))
  .force('link',d3.forceLink().id(node => node.name))
  .on('tick',update)


simulation.nodes(graph.nodes)
simulation.force('link').links(graph.links)

canvas.call(d3.drag()
  .container(canvas.node())
  .subject(dragsubject).on('start',dragstarted)
  .on('drag',dragged).on('end',dragended)
)



function update() {
  ctx.clearRect(0,width,height)

  ctx.beginPath()
  ctx.globalAlpha = 0.5
  ctx.strokeStyle = 'blue'
  graph.links.forEach(drawLink)
  ctx.stroke()


  ctx.beginPath()
  ctx.globalAlpha = 1
  graph.nodes.forEach(drawNode)
  ctx.fill()
}



function dragsubject(event) {
  return simulation.find(event.x,event.y);
}

function drawNode(d) {

  ctx.fillStyle = color(d.party)
  ctx.moveto(d.x,d.y)
  ctx.arc(d.x,d.y,r,Math.PI * 2)
}

function drawLink(l) {
  ctx.moveto(l.source.x,l.source.y)
  ctx.lineto(l.target.x,l.target.y)
}

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

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

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

update()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.js"></script>
<canvas id="network" width="600" height="300"></canvas>

解决方法

来自documentation

simulation.find(x,y [,radius])- 返回具有给定搜索半径的最接近位置⟨x,y⟩的节点。如果未指定radius,则默认为无穷大。如果搜索区域内没有节点,则返回undefined。

传递r的半径(与球的半径相同)使得仅当动作是从节点中心到r时才记录拖动。

const graph = {
  nodes: [{
      name: 'john',age: 35
    },{
      name: 'simon',age: 37
    },{
      name: 'manjoor',{
      name: 'lorg',age: 34
    },{
      name: 'kilvin',age: 32
    },],links: [{
      source: 'john',target: 'simon'
    },{
      source: 'john',target: 'manjoor'
    },{
      source: 'simon',target: 'lorg'
    },target: 'kilvin'
    },{
      source: 'manjoor',{
      source: 'lorg',target: 'john'
    },{
      source: 'kilvin',]
}


const canvas = d3.select('#network')

const width = canvas.attr('width')
const height = canvas.attr('height')
const r = 30
const ctx = canvas.node().getContext('2d')

const color = d3.scaleOrdinal(d3.schemeAccent);

const simulation = d3.forceSimulation()
  .force('x',d3.forceX(width / 2))
  .force('y',d3.forceY(height / 2))
  .force('collide',d3.forceCollide(r + 20))
  .force('charge',d3.forceManyBody().strength(-100))
  .force('link',d3.forceLink().id(node => node.name))
  .on('tick',update)


simulation.nodes(graph.nodes)
simulation.force('link').links(graph.links)

canvas.call(d3.drag()
  .container(canvas.node())
  .subject(dragsubject)
  .on('start',dragstarted)
  .on('drag',dragged)
  .on('end',dragended)
)

function update() {
  ctx.clearRect(0,width,height)

  ctx.beginPath()
  ctx.globalAlpha = 0.5
  ctx.strokeStyle = 'blue'
  graph.links.forEach(drawLink)
  ctx.stroke()

  ctx.beginPath()
  ctx.globalAlpha = 1
  graph.nodes.forEach(drawNode)
  ctx.fill()
}

function dragsubject(event) {
  return simulation.find(event.x,event.y,r);
}

function drawNode(d) {
  ctx.fillStyle = color(d.party)
  ctx.moveTo(d.x,d.y)
  ctx.arc(d.x,d.y,r,Math.PI * 2)
}

function drawLink(l) {
  ctx.moveTo(l.source.x,l.source.y)
  ctx.lineTo(l.target.x,l.target.y)
}

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

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

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

update()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.js"></script>
<canvas id="network" width="600" height="300"></canvas>