D3: Grafici ad albero - Pan e zoom

    let svg = d3.select("body").append("svg")
            .attr("width",670).attr("height",300)

let margin = { top: 5, right: 135, bottom: 5, left: 50 }
let innerWidth = 670 - margin.left - margin.right
let innerHeight = 300 - margin.top - margin.bottom

let outer = svg.append('g')
               .attr('transform', `translate(${margin.left},${margin.top})`)
let graph = outer.append('g')
let zoomBehavior = d3.zoom()
                     .on('zoom', e => graph.attr('transform', e.transform))
svg.call(zoomBehavior)

d3.json("menu.json").then(data => {
    
    let root = d3.hierarchy(data)
    let treeLayout = d3.tree().size([innerHeight, innerWidth])
    treeLayout(root)
    
    let links = root.links()
    let linkGenerator = d3.linkHorizontal()
                          .x(d => d.y)
                          .y(d => d.x)
                          
    graph.selectAll('path')
         .data(links).join('path')
         .attr('d', linkGenerator)
         .attr("fill", "none")
         .attr("stroke", "Chocolate")
  
    graph.selectAll('text')
         .data(root.descendants()).join('text')
         .text(d => d.data.name)
         .attr('x', d => d.y)
         .attr('y', d => d.x)
         .attr('dy', '0.2em')
         .attr('font-size', d => d.height>0 ? 28-d.depth*4 : 14)
         .attr('text-anchor', d => d.children ? 'middle' : 'start')
         .style("text-shadow", "1px 1px 2px white, -1px -1px 2px white")
})
  

Attraverso il modulo d3-zoom è possibile creare un gestore di eventi (zoomBehavior) per la “navigazione” del grafico. Le trasformazioni vengono calcolate automaticamente ma è necessario indicare a quale elemento applicarle in fase di creazione (graph nell’esempio). L’oggetto creato va poi fatto chiamare dall’elemento contenitore (svg nell’esempio).

L’aggiunta di un contenitore intermedio (outer) serve a mantenere le trasformazioni di layout che, altrimenti, andrebbero perse alla prima interazione.


Strutturazione dei file: