D3+react realizes the horizontal organization chart that can be enlarged, reduced, refreshed and downloaded
The code implementation is shown in the figure:
Conditions, the following modules need to be introduced:
import * as d3 from 'd3'
import saveSvg from 'save svg as png' / / download svg into png image format
The implementation code is as follows:
import React, { Component } from 'react'
import * as d3 from 'd3'
import saveSvg from 'save-svg-as-png'
//Transition time
const duration = 0
export default class modal extends Component {
constructor(props){
super(props)
this.constNode = []; // Large node data
this.constRelation = []; // Small node data
this.resData = {};
this.layoutTree = ‘’; // Generating structure
this.diamonds = ‘’; // Square shape
this.i = 0; // Subscript of display node
this.hasChildNodeArr = []; // Element array in tree
this.originDiamonds = ‘’; // Source object
this.tree = {}; // Component structure
this.rootUp = ‘’; // Initialization starting point
this.rootDown = ‘’; // Initialization starting point
this.svg = ‘’; // Main graph
this.nodeData = {}; // Node information
this.isShowloading = true; // false in the penetration diagram indicates that the content loading is completed
this.isShowDetail = false; // False in the penetration diagram indicates that the content loading is completed
this.isShowExportPng = true / / the Save button is displayed
this.zoom = null;
}
componentDidMount() {
var treeData = [
{
"name": "China",
"children": [
{
"name": "Henan",
"children": [
{
"name": "Zhengzhou",
},
{
"name": "Hebi",
},
]
},
{
"name": "Shandong",
"children": [
{
"name": "Jinan",
},
{
"name": "Texas",
},
]
},
{
"name": "Shanghai",
"children": [
{
"name": "Pudong New Area",
}
]
},
]
}
];
let svgW = document.body.clientWidth
let svgH = document.body.clientHeight
//Square shape
this.diamonds = {
w: 175,
h: 68,
intervalW: 200,
intervalH: 150
}
/// /. size() sets the available size of the tree, so they may be compressed and overlapped according to the spacing between cousin nodes, cousin nodes, etc
/// / using. nodeSize() only means that each node should have such a large space, so they will never overlap!
this.layoutTree = d3.tree().nodeSize([this.diamonds.intervalW, this.diamonds.intervalH])
.separation(() => 0.5)
this.zoom = d3.zoom() .scaleExtent([0.5, 3]) .on("zoom", ()=>{ // zoom event this.svg.attr("transform", `${d3.event.transform.translate(svgW / 4,200)} scale(${d3.zoomTransform(d3.select("svg").node()).k})`); }); this.svg = d3.select('#lgwTree').append('svg').attr('width', svgW).attr('height', svgH).attr('id', 'treesvg') .attr('xmlns', 'http://www.w3.org/2000/svg') .call(this.zoom) .on('dblclick.zoom', null) .attr('style', 'position: relative;z-index: 2;') .append('g').attr('id', 'gAll') .attr('transform', 'translate(' + (svgW / 4) + ',' + (200) + ')') this.rootUp = d3.hierarchy(treeData[0]) this.update(this.rootUp);
}
diagonal (d) {
// console.log(d)
//The tree is shown to the right
return "M"+d.source.y+" "+d.source.x+
"L"+(d.source.y+120)+" "+(d.source.x)+
" L"+(d.source.y+120)+" "+(d.target.x)+" L"+
d.target.y+" "+(d.target.x);
//The tree is displayed downward
// return "M"+(d.source.x+10)+" "+d.source.y+
// "L"+(d.source.x+10)+" "+(d.target.y-50)+
// " L"+(d.target.x+10)+" "+(d.target.y-50)+" L"+
// (d.target.x+10)+" "+(d.target.y);
}
click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
this.update(d);
}
update(source) {
let _this=this;
if (source.parents === null) {
source.isOpen = !source.isOpen
}//Initialize deployment
let nodes = this.layoutTree(this.rootUp).descendants(); let links = this.layoutTree(this.rootUp).links(); // Normalize for fixed depth. Set the y coordinate point, each layer accounting for 180px nodes.forEach(function(d) { d.y = d.depth * 180; }); // Update the nodes... Each node corresponds to a group var node = this.svg.selectAll("g.node") .data(nodes, function(d) { return d.id || (d.id = ++this.i); });//data(): bind an array to the selection set, and each value of the array is bound to each element of the selection set // Enter any new nodes at the parent's previous position var nodeEnter = node.enter().append("g") //Add a g to svg. G is an attribute in svg, which means group. It represents a group of things, such as lines, rects and circles. In fact, the coordinate axis is composed of these things. .attr("class", "node") //attr sets the html attribute and style sets the css attribute .attr('transform',d => "translate(" +( d.y) + "," + (d.x) + ")") .on("click", (d)=>{ // this.click(d) }); // Create circle plus minus sign nodeEnter.append('circle') .attr('type', d => d.id || (d.id = 'text' + ++this.i)) .attr('r', d => d._children || d.children ? '9' : '0') .attr('cy', d => 0) .attr('cx', d => 55) .attr('class', 'circle') .attr('fill', '#fff ') / / fill colors of several circles in the initial display .attr('stroke', d =>'#128BED ') / / border color of all circles .on('click', function(d){ _this.click(d) setTimeout(() => { if (this.innerHTML === '-') { d.isOpen = false this.innerHTML = '+' } else { d.isOpen = true this.innerHTML = '-' } }, 0) }) // symbols for add and subtract nodeEnter.append('svg:text') .attr('type', d => d.id || (d.id = 'text' + ++this.i)) .on('click', function(d){ _this.click(d) setTimeout(() => { if (this.innerHTML === '+') { d.isOpen = false this.innerHTML = '-' } else { d.isOpen = true this.innerHTML = '+' } }, 0) }) .attr('x', 55) //eslint-disable-next-line .attr('dy', d => 5) .attr('text-anchor', 'middle') .attr('font-size', '18') .attr('fill', '#128BED ') / / color with minus sign inside the circle //eslint-disable-next-line .text(d => d.data.children?(d.isOpen?'+' : '-'): '') nodeEnter.append("rect") .attr("x",-23) .attr("y", -10) .attr("width",70) .attr("height",22) .attr("rx",10) .style("fill", "#357CAE");//d represents data, that is, the data bound to an element. // Add arrow nodeEnter.append('marker') .attr("id","arrow") .attr("markerUnits","strokeWidth") .attr("markerWidth","12") .attr("markerHeight","12") .attr("viewBox","0 0 12 12") .attr("refX",d=>{ return '38' }) .attr("refY",6) .attr("orient","auto") .attr('stroke-width', 2) //Arrow Width .append('path') .attr('d', 'M2,0 L14,6 L2,12 L5,6 L2,0') // Path of arrow .attr('fill', '#128BED ') / / arrow color //Add label nodeEnter.append("text") .attr("x", function(d) { return d.children || d._children ? 13 : 13; }) .attr("dy", "6") .attr("text-anchor", "middle") .text(d=>d.data.name) .attr('font-size', 12) .style("fill", "white") .attr('cursor', 'pointer') .style("fill-opacity", 1); // Transition nodes to their new position //node is the reserved data set, which adds transition animation to the graphics of the original data. The first is the position of the whole group var nodeUpdate = node.transition() //Start an animation transition .duration(duration) //Transition delay time, which mainly sets the transition delay of circle nodes with slashes .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); nodeUpdate.select("rect") .attr("x",-23) .attr("y", -10) .attr("width",70) .attr("height",22) .attr("rx",10) .style("fill", "#357CAE"); nodeUpdate.select("text") .attr("text-anchor", "middle") .style("fill-opacity", 1); // Transition existing nodes to the parent's new position. //Finally, process the missing data and add the missing animation var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); // nodeExit.select("circle") // .attr("r", 1e-6); nodeExit.select("rect") .attr("x",-23) .attr("y", -10) .attr("width",70) .attr("height",22) .attr("rx",10) .style("fill", "#357CAE"); nodeExit.select("text") .attr("text-anchor", "middle") .style("fill-opacity", 1e-6); // Update the links... Related to line operation //Reprocess connection set var link = this.svg.selectAll("path.link") .data(links, function(d) { return d.target.id; }); // Enter any new links at the parent's previous position. //Add a new connection link.enter().insert("path", "g") .attr("class", "link") .attr("d", (d)=> { return this.diagonal(d) }) .attr('stroke', '#777 ') / / line color .style('fill-opacity', 0) // The transparency of the area offset from the line when the line is drawn .attr('marker-end', 'url(#arrow)'); // Transition links to their new position //Add transition animation to reserved lines link.transition() .duration(duration) .attr('d', d => this.diagonal(d)) // Transition existing nodes to the parent's new position. //Add transition animation to missing lines link.exit().transition() .duration(duration) .attr("d", (d) =>{ return this.diagonal(d) }) .remove(); // Stash the old positions for transition nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; });
}
//Save picture
downloadPng () {
const base64Data = document.getElementById('treesvg')
const result = base64Data.getBBox()
const width = result.width
const height = result.height
const top = result.y
const left=result.x
const backgroundColor = '#fff'
const opts = {
backgroundColor, / / create PNG with the given background color. The default is transparent
left: left - 50, / / specifies the left side of the viewbox location. The default is 0
top: top - 100, / / specifies the top of the viewbox location. The default is 0
width: width + 100, / / specifies the width of the image. If yes is given, or the boundary of the element width, or the CSS of the element width, or the calculated width of 0, the default is,.
//scale: 1, / / change the resolution of the output PNG. The default is 1, the same dimension as the source SVG.
height: height + 200, / / specifies the height of the image. If the given is, or the boundary of the element height, or the CSS of the element height, or the calculated height of 0, the default is,.
encoderType: 'image/png',
Encoderoptions: 1, / / the number between 0 and 1 indicates the image quality. The default value is 0.8
}
saveSvg.saveSvgAsPng(base64Data, ${'tree view'} ${'. PNG'}, opts)
}
//Refresh page
refresh=()=>{
console.log('refresh ');
d3.select("svg").call(this.zoom.transform, d3.zoomIdentity);
}
scaleBig(){
console.log('zoom in ');
this.zoom.scaleBy(d3.select("svg"), 1.1); / / the zoom event will be triggered after the method is executed
}
scaleSmall(){
console.log('zoom out ');
this.zoom.scaleBy(d3.select("svg"), 0.9); / / the zoom event will be triggered after the method is executed
}
render() {}
}
**Note: * * the code is redundant or insufficient, please give more advice...