D3 Collapse Tree отображает тысячи узлов - PullRequest
1 голос
/ 03 апреля 2020

Я работаю с D3 для рендеринга разборного дерева, содержащего тысячи узлов. На 1-й глубине только приблизительно 20 узлов; проблема возникает после расширения этих узлов. Некоторые из узлов глубины 2 содержат сотни дочерних элементов, поэтому они начинают go из поля зрения. Я не могу понять, как решить эту проблему. Я довольно новичок в работе с D3 и SVG в целом, любые ресурсы могут помочь.

Мои вопросы:

  1. Как я могу сделать svg-оболочку для моего дерева более динамичной c так что он расширяется при расширении моего дерева?
  2. Положение моего дерева при начальной загрузке довольно низкое, как мне его отцентрировать?

Любые рекомендации по настройке положения дерева дерево для размещения многих узлов будет оценено.

Мой JSFiddle

onLoad()

async function onLoad() {
  const width = 960
  const height = document.body.clientHeight
  const dx = 10
  const dy = 156

  const metrics = getData()
  const root = d3.hierarchy(metrics)
  console.log(root)

  const margin = {
    top: 10,
    right: 120,
    bottom: 10,
    left: 40
  }
  const diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x)

  root.x0 = dy / 2
  root.y0 = 0

  root.descendants().forEach((d, i) => {
    d.id = i
    d._children = d.children
    // collapse(d)
    if (d.depth && d.depth <= 1) d.children = null
  })

  const svg = d3.select('svg')
    // .attr('viewbox', [-margin.left, -margin.top, width, dx])
    .style('user-select', 'none')

  const gNode = svg.append('g')
    .attr('cursor', 'pointer')
    .attr('pointer-events', ' all')
    .attr('width', width)
    .attr('height', height)

  const tree = d3.tree().nodeSize([dx, dy])

  function update(source) {
    const duration = d3.event && d3.eventaltKey ? 2500 : 250
    const nodes = root.descendants().reverse()
    const links = root.links()
    console.log('updating tree...')

    tree(root) // compute new tree

    let left = root
    let right = root
    root.eachBefore(n => {
      if (n.x < left.x) left = n
      if (n.x > right.x) right = n
    })

    const height = right.x - left.x + margin.top + margin.bottom
    console.log(left.x)
    const transition = svg.transition()
      .duration(duration)
      .attr('viewBox', [-margin.left, left.x - margin.top, width / 2, height / 2])
      .tween('resize', window.ResizeObserver ? null : () => () => svg.dispatch('toggle'))

    // Update the nodes…
    const node = gNode.selectAll('g')
      .data(nodes, d => d.id)

    // Enter any new nodes at the parent's previous position.
    const nodeEnter = node.enter().append('g')
      .attr('transform', d => `translate(${source.y0},${source.x0})`)
      .attr('fill-opacity', 0)
      .attr('stroke-opacity', 0)
      .on('click', d => {
        d.children = d.children ? null : d._children
        update(d)
      })

    nodeEnter.append('circle')
      .attr('r', d => d._children ? 2.5 : 1)
      .attr('fill', d => d._children ? '#555' : '#999')
      .attr('stroke-width', 2)

    nodeEnter.append('text')
      .attr('dy', '0.31em')
      .attr('x', d => d._children ? -10 : 10)
      .attr('text-anchor', d => d._children ? 'end' : 'start')
      .attr('font-size', d => d.depth === 0 ? 5 : 10 / (d.depth * 1.3))
      .text(d => {
        console.log(d)
        return d.data.title
      })
      .clone(true).lower()
      .attr('stroke-linejoin', 'round')
      .attr('stroke-width', 3)
      .attr('stroke', 'white')

    // Transition nodes to their new position.
    const nodeUpdate = node.merge(nodeEnter).transition(transition)
      .attr('transform', d => `translate(${d.y},${d.x})`)
      .attr('fill-opacity', 1)
      .attr('stroke-opacity', 1)

    // Transition exiting nodes to the parent's new position.
    const nodeExit = node.exit().transition(transition).remove()
      .attr('transform', d => `translate(${source.y},${source.x})`)
      .attr('fill-opacity', 0)
      .attr('stroke-opacity', 0)

    // Update the links…
    const link = gNode.selectAll('path')
      .data(links, d => d.target.id)

    // Enter any new links at the parent's previous position.
    const linkEnter = link.enter().append('path')
      .attr('d', d => {
        const o = {
          x: source.x0,
          y: source.y0
        }
        return diagonal({
          source: o,
          target: o
        })
      })

    // Transition links to their new position.
    link.merge(linkEnter).transition(transition)
      .attr('d', diagonal)

    // Transition exiting nodes to the parent's new position.
    link.exit().transition(transition).remove()
      .attr('d', d => {
        const o = {
          x: source.x,
          y: source.y
        }
        return diagonal({
          source: o,
          target: o
        })
      })

    // Stash the old positions for transition.
    root.eachBefore(d => {
      d.x0 = d.x
      d.y0 = d.y
    })
  }
  update(root)
}

Редактировать

Работая с getBBox при вставке и обновлении, я не уверен, как к этому подойти.

  const svg = d3.select('svg')
    // .attr('viewbox', [-margin.left, -margin.top, width, dx])
    .style('user-select', 'none')


  const gNode = svg.append('g')
    .attr('cursor', 'pointer')
    .attr('pointer-events', ' all')
/*     .attr('width', width)
    .attr('height', height) */

  const tree = d3.tree().nodeSize([dx, dy])

  const { x, y, w, h } = gNode.getBBox()
  svg.attr('viewbox', [x, y, width, height])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...