Using pan but not zoom on D3

31 Views Asked by At

I've used D3 to create a force directed graph with dynamic fields. It's currently has zoom installed, but I'm looking to remove the zoom but keep the pan. I've seen the use of xyzoom but I'm unsure how I can translate this into the code or if there's any better alternatives versions available Any advice would be much appreciated

<svg width="100%" height="100%"></svg>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var baseNodes = [
      //Base node here
]

var baseLinks = [
      //Base links here
]

var nodes = [...baseNodes]
var links = [...baseLinks]

function getNeighbors(node) {
  return baseLinks.reduce(function (neighbors, link) {
          if (link.target.id === node.id) {
            neighbors.push(link.source.id)
          } else if (link.source.id === node.id) {
            neighbors.push(link.target.id)
          }
          return neighbors
      },
      [node.id]
  )
}

function isNeighborLink(node, link) {
  return link.target.id === node.id || link.source.id === node.id
}


function getNodeColor(node, neighbors) {
  if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
      return node.level === 1 ? 'blue' : 'green'
  }

  return node.level === 1 ? 'red' : 'gray'
}


function getLinkColor(node, link) {
  return isNeighborLink(node, link) ? 'green' : '#E5E5E5'
}

function getTextColor(node, neighbors) {
  return Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1 ? 'green' : 'black'
}

var width = window.innerWidth
var height = window.innerHeight

var svg = d3.select('svg')
      .attr("width",  1920)
      .attr("height",  1080)
      .call(d3.zoom().on("zoom", function () {
           svg.attr("transform", d3.event.transform)
      }))
  .append("g")

var linkElements,
  nodeElements,
  textElements

var linkGroup = svg.append('g').attr('class', 'links')
var nodeGroup = svg.append('g').attr('class', 'nodes')
var textGroup = svg.append('g').attr('class', 'texts')

var selectedId

var linkForce = d3
  .forceLink()
  .id(function (link) { return link.id })
  .strength(function (link) { return link.strength })

var simulation = d3
  .forceSimulation()
  .force('link', linkForce)
  .force('charge', d3.forceManyBody().strength(-2000))
  .force('center', d3.forceCenter(width / 1.5, height / 1.5))

var dragDrop = d3.drag().on('start', function (node) {
  node.fx = node.x
  node.fy = node.y
}).on('drag', function (node) {
  simulation.alphaTarget(1).restart()
  node.fx = d3.event.x
  node.fy = d3.event.y
}).on('end', function (node) {
  if (!d3.event.active) {
      simulation.alphaTarget(0.05)
  }
  node.fx = null
  node.fy = null
})

function selectNode(selectedNode) {
  if (selectedId === selectedNode.id) {
      selectedId = undefined
      resetData()
      updateSimulation()
  } else {
      selectedId = selectedNode.id
      updateData(selectedNode)
      updateSimulation()
  }

  var neighbors = getNeighbors(selectedNode)

  nodeElements.attr('fill', function (node) { return getNodeColor(node, neighbors) })
  textElements.attr('fill', function (node) { return getTextColor(node, neighbors) })
  linkElements.attr('stroke', function (link) { return getLinkColor(selectedNode, link) })
}

function resetData() {
  var nodeIds = nodes.map(function (node) { return node.id })

  baseNodes.forEach(function (node) {
      if (nodeIds.indexOf(node.id) === -1) {
          nodes.push(node)
      }
  })

  links = baseLinks
}

function updateData(selectedNode) {
  var neighbors = getNeighbors(selectedNode)
  var newNodes = baseNodes.filter(function (node) {
      return neighbors.indexOf(node.id) > -1 || node.level === 1
  })

  var diff = {
      removed: nodes.filter(function (node) { return newNodes.indexOf(node) === -1 }),
      added: newNodes.filter(function (node) { return nodes.indexOf(node) === -1 })
  }

  diff.removed.forEach(function (node) { nodes.splice(nodes.indexOf(node), 1) })
  diff.added.forEach(function (node) { nodes.push(node) })

  links = baseLinks.filter(function (link) {
      return link.target.id === selectedNode.id || link.source.id === selectedNode.id
  })
}

function updateGraph() {
  linkElements = linkGroup.selectAll('line')
      .data(links, function (link) {
          return link.target.id + link.source.id
      })

  linkElements.exit().remove()

  var linkEnter = linkElements
      .enter().append('line')
      .attr('stroke-width', 1)
      .attr('stroke', 'rgba(50, 50, 50, 0.2)')

  linkElements = linkEnter.merge(linkElements)

  nodeElements = nodeGroup.selectAll('circle')
      .data(nodes, function (node) { return node.id })

  nodeElements.exit().remove()

  var nodeEnter = nodeElements
      .enter()
      .append('circle')
      .attr('r', 5)
      .attr('fill', function (node) { return node.level === 1 ? 'red' : 'gray' })
      .call(dragDrop)
      .on('click', selectNode)

  nodeElements = nodeEnter.merge(nodeElements)

  textElements = textGroup.selectAll('text')
      .data(nodes, function (node) { return node.id })

  textElements.exit().remove()

  var textEnter = textElements
      .enter()
      .append('text')
      .text(function (node) { return node.label })
      .attr('font-size', 16)
      .attr('dx', 20)
      .attr('dy', 16)

  textElements = textEnter.merge(textElements)
}

function updateSimulation() {
  updateGraph()

  simulation.nodes(nodes).on('tick', () => {
      nodeElements
          .attr('cx', function (node) { return node.x })
          .attr('cy', function (node) { return node.y })
      textElements
          .attr('x', function (node) { return node.x })
          .attr('y', function (node) { return node.y })
      linkElements
          .attr('x1', function (link) { return link.source.x })
          .attr('y1', function (link) { return link.source.y })
          .attr('x2', function (link) { return link.target.x })
          .attr('y2', function (link) { return link.target.y })
  })

  simulation.force('link').links(links)
  simulation.alphaTarget(0.05).restart()
}

updateSimulation()

</script>
1

There are 1 best solutions below

0
Peter F On

You can turn off zooming using scaleExtent().

Reference: https://d3js.org/d3-zoom#zoom_scaleExtent

Example:

var svg = d3.select('svg')
    .attr("width",  1920)
    .attr("height",  1080)
    .call(
        d3.zoom()
            .scaleExtent(1, 1)
            .on("zoom", function () {
                svg.attr("transform", d3.event.transform)
            })
    )
    .append("g")