d3+js横向图

<!doctype html>
<html>
<head>
    <meta charset="utf-8">

    <!-- <link rel="stylesheet" type="text/css" href="gqct.css"> -->
    <title>d3+jQuery横向树图</title>
    <script src="https://cdn.bootcss.com/d3/3.2.7/d3.min.js"></script>
    <script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script>
</head>

<body>
<div class="container" id="treecontainer">
    <div class="menu">
        <div id="product_tree"></div>
    </div>
</div>
</body>
<script type="text/javascript">
    var container;
    var zoom;
    var rootData;
    var depthInfo;
    var rootName;//根节点名称

    jQuery(document).ready(function () {
        /* resizeScreen();*/

        //selectChange();
        var child=document.getElementById("product_tree");
        child.innerHTML = '';
        getData();
    });

    var getData =function() {
        rootData ={
    "downward": {
        "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
        "children": [
            {
                "isExpand": true,
                "amount": "物流送往公司",
                "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
                "children": [
                    {
                        "isExpand": true,
                        "amount": "物流送达公司",
                        "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
                        "children": [
                            {
                                "isExpand": true,
                                "amount": "00000021",
                                "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
                                "children": [
                                    
                                ],
                                "hasChildren": true,
                                "name": "00000021样本指派到实验室",
                                "text": "",
                                "hasHumanholding": true,
                                "ratio": ""
                            },
                            {
                                "isExpand": true,
                                "amount": "2020-08-06 09:36:28",
                                "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
                                "children": [
                                    
                                ],
                                "hasChildren": true,
                                "name": "物流测试账号派送中",
                                "text": "",
                                "hasHumanholding": true,
                                "ratio": "00000021"
                            },
                            {
                                "isExpand": true,
                                "amount": "2020-08-06 09:40:09",
                                "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
                                "children": [
                                    
                                ],
                                "hasChildren": true,
                                "name": "技术员测试账号审核通过",
                                "text": "驻点",
                                "hasHumanholding": true,
                                "ratio": "00000021"
                            },
                            {
                                "isExpand": true,
                                "amount": "2020-08-06 10:28:00",
                                "orderId": "94970e083e9c4b36a821fc0ebd65cf7b",
                                "children": [
                                    
                                ],
                                "hasChildren": true,
                                "name": "技术员测试账号质检通过",
                                "text": "",
                                "hasHumanholding": true,
                                "ratio": "00000021"
                            }
                        ],
                        "hasChildren": true,
                        "name": "测试账号签收",
                        "text": "",
                        "hasHumanholding": true,
                        "ratio": "2020-08-06"
                    }
                ],
                "hasChildren": true,
                "name": "物流测试账号签收",
                "text": "22222",
                "hasHumanholding": true,
                "ratio": "12312312312"
            }
        ],
        "name": "origin",
        "direction": "downward"
    }
}
        rootName = '科技有限公司';
        drawing();
    };
    var	drawing = function() {
        var _this = this;
        // var rootName = ''; //根节点的名字
        var rootRectWidth = 0; //根节点rect的宽度
        var forUpward = true;

        var treeChart = function(d3Object) {
            this.d3 = d3Object;
            this.directions = ['downward'];
        };


        treeChart.prototype.drawChart = function() {
            // First get tree data for both directions.
            this.treeData = {};
            var self = this;
            self.directions.forEach(function(direction) {
                self.treeData[direction] = _this.rootData[direction];
            });
            // rootName = '北京伴学科技有限公司';
            rootRectWidth = _this.rootName.length * 15;
            self.graphTree(self.getTreeConfig());
        };

        treeChart.prototype.getTreeConfig = function() {
            var treeConfig = {
                'margin': {
                    'top': 10,
                    'right': 5,
                    'bottom': 0,
                    'left': 5
                }
            }

            treeConfig.chartWidth = (1600 - treeConfig.margin.right - treeConfig.margin.left);
            treeConfig.chartHeight = (800 - treeConfig.margin.top - treeConfig.margin.bottom);
            treeConfig.centralHeight = treeConfig.chartHeight / 2;
            treeConfig.centralWidth = treeConfig.chartWidth / 6;
            treeConfig.linkLength = 300;
            treeConfig.duration = 500; //动画时间
            return treeConfig;
        };

        treeChart.prototype.graphTree = function(config) {


            var self = this;
            var d3 = this.d3;
            var linkLength = config.linkLength;
            var duration = config.duration;
            var hasChildNodeArr = [];
            var id = 0;
            var diagonal = d3.svg.diagonal()
                .projection(function(d) {
                    return [d.y, d.x];
                });
            var pathFunc;
            if (false) {
                pathFunc = diagonal;
            } else {
                pathFunc = funLine;
            }
            var zoom = d3.behavior.zoom()
                .scaleExtent([0.5, 2])
                .on('zoom', redraw);
            var svg = d3.select('#product_tree')
                .append('svg')
                .attr('width', config.chartWidth + config.margin.right + config.margin.left)
                .attr('height', config.chartHeight + config.margin.top + config.margin.bottom)
                .attr('xmlns','http://www.w3.org/2000/svg')
                .on('mousedown', disableRightClick)
                .call(zoom)
                .on('dblclick.zoom', null);
            var treeG = svg.append('g')
                .attr('class', 'gbox')
                .attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');

            //箭头
            var markerDown = svg.append("marker")
                .attr("id", "resolvedDown")
                .attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化
                .attr("markerUnits", "userSpaceOnUse")
                .attr("viewBox", "0 -5 10 10") //坐标系的区域
                .attr("refX", 0) //箭头坐标
                .attr("refY", 0)
                .attr("markerWidth", 12) //标识的大小
                .attr("markerHeight", 12)
                .attr("orient", "0") //绘制方向,可设定为:auto(自动确认方向)和 角度值
                .attr("stroke-width", 2) //箭头宽度
                .append("path")
                .attr("d", "M0,-5L10,0L0,5") //箭头的路径
                .attr('fill', '#000'); //箭头颜色

            // Initialize the tree nodes and update chart.
            for(var d in this.directions) {
                var direction = this.directions[d];
                var data = self.treeData[direction];
                if (typeof(data) != "undefined") {
                    data.x0 = config.centralHeight;
                    data.y0 = config.centralWidth;
                    data.children.forEach(collapse);
                    console.log(treeG)
                    update(data, data, treeG);
                }
            }
            function update(source, originalData, g) {
                console.log(source)
                var direction = originalData['direction'];
                var node_class = direction + 'Node';
                var link_class = direction + 'Link';
                var downwardSign =1;
                var nodeColor = '#8b4513';

                var isExpand = false;
                var nodeSpace = 160;
                var tree = d3.layout.tree().sort(sortByDate).nodeSize([nodeSpace, 0]);
                var nodes = tree.nodes(originalData);
                var links = tree.links(nodes);
                var offsetY = -config.centralHeight;

                nodes.forEach(function(d) {
                    d.y = downwardSign * (d.depth * linkLength) + config.centralWidth;
                    d.x = d.x - offsetY;
                    if(d.name == 'origin') {
                        d.x = config.centralHeight;
                        d.y += downwardSign * 0; // 左右两树图根节点之间的距离
                    }
                });

                // Update the node.
                var node = g.selectAll('g.' + node_class)
                    .data(nodes, function(d) {
                        return d.id || (d.id = ++id);
                    });
                var nodeEnter = node.enter().append('g')
                    .attr('class', node_class)
                    //			.classed('alink',true)//为选择名称而添加的统一class
                    .attr('data-text', function(d) {
                        return d.name;
                    })
                    .attr('transform', function(d) {
                        return 'translate(' + source.y0 + ',' + source.x0 + ')';
                    })
                    .style('cursor', function(d) {
                        return(d.name == 'origin') ? '' : (d.children || d._children) ? 'pointer' : '';
                    });
                // .on('click', click);


                nodeEnter.append("svg:rect")
                    .attr("x", function(d) {
                        return(d.name == 'origin') ? -(rootRectWidth / 2) : 95;
                    })
                    .attr("y", function(d) {
                        return(d.name == 'origin') ? -20 : -16;
                    })
                    .attr("width", function(d) {
                        return(d.name == 'origin') ? rootRectWidth : 150;
                    })
                    .attr("height", 40)
                    .attr("rx", 10)
                    .style("stroke", function(d) {
                        return(d.name == 'origin') ? "#0505ee" : "#CCC";//外边框颜色
                    })
                    .on('click',function(d) {
                        get_click(d);//节点的点击事件 d为节点自身内容
                    })
                    .style("fill", function(d) {
                        return(d.name == 'origin') ? "#0080E3" :  "#FFF";//节点背景色
                    });

                nodeEnter.append('circle')
                    .attr('r', 1e-6);
                nodeEnter.append("text")
                    .attr("class", "linkname")
                    .attr("x",function(d) {
                        return(d.name == 'origin') ? '0' : '170';
                    })
                    .attr('dy', function(d) {
                        return(d.name == 'origin') ? '.35em' : '-3';
                    })
                    .attr("text-anchor",'middle')
                    .attr('fill', '#337ab7')
                    .text(function(d) {
                        if(d.name == 'origin') {
                            // return ((forUpward) ? '根节点TOP' : '根节点Bottom');
                            return _this.rootName;
                        }
                        if(d.repeated) {
                            return '[Recurring] ' + d.name;
                        }
                        return(d.name.length > 10) ? d.name.substr(0, 10) : d.name;
                    })
                    .style({
                        'fill-opacity': 1e-6,
                        'fill': function(d) {
                            if(d.name == 'origin') {
                                return '#fff';
                            }
                        },
                        'font-size': function(d) {
                            return(d.name == 'origin') ? 14 : 13;
                        },
                        'cursor': "pointer"
                    })
                    .on('click', Change_modal);

                nodeEnter.append("text")
                    .attr("class", "linkname")
                    .attr("x",function(d) {
                        return(d.name == 'origin') ? '0' : '170';
                    })
                    .attr("dy", function(d) {
                        return(d.name == 'origin') ? '0' :'9';
                    })
                    .attr("text-anchor",'middle')
                    .text(function(d) {
                        return d.name.substr(10, d.name.length);
                    })
                    .style({
                        'fill': '#337ab7',
                        'font-size': function(d) {
                            return(d.name == 'origin') ? 14 : 13;
                        },
                        'cursor': "pointer"
                    });
                    /*.on('click',function(d) {
                      
                    });*/
                nodeEnter.append("text")
                    .attr("x",function(d) {
                        return(d.name == 'origin') ? '0' : '170';
                    })
                    .attr("dy", function(d) {
                        return(d.name == 'origin') ? '.35em' :  '20';
                    })
                    .attr("text-anchor",'middle')
                    .attr("class", "linkname")
                    .style("fill", "#7F7F7F")
                    .style('font-size', 10)
                    .text(function(d) {
                        var str = (d.name == 'origin') ? '' :d.amount;
                        return(str.length > 13) ? str.substr(0, 13) + ".." : str;
                    });
                nodeEnter.append("text")
                    .attr("x", "0")
                    .attr("dy", function(d) {
                        return(d.name == 'origin') ? '.35em' :'-5';
                    })
                    .attr("text-anchor", "start")
                    .attr("class", "linkname")
                    .style("fill", "green")
                    .style('font-size', 10)
                    .style("width", "500")
                    .text(function(d) {
                        return(d.name == 'origin') ? "" : d.ratio;
                    });

                nodeEnter.append("text")
                    .attr("x", '35')
                    .attr("dy", function(d) {
                        return(d.name == 'origin') ? '.35em' :'15';
                    })
                    .attr("text-anchor", "start")
                    .attr("class", "linkname")
                    .style("fill", "#0ba4ec")
                    .style('font-size', 10)
                    .text(function(d) {
                        return(d.name == 'origin') ? "" : d.text;
                    });


                // Transition nodes to their new position.原有节点更新到新位置
                var nodeUpdate = node.transition()
                    .duration(duration)
                    .attr('transform', function(d) {
                        return 'translate(' + d.y + ',' + d.x + ')';
                    });
                nodeUpdate.select('circle')
                    .attr('r', function(d) {
                        return(d.name == 'origin') ? 0 : (hasChildNodeArr.indexOf(d) == -1) ? 0 : 6;
                    })
                    .attr('cx', function(d) {
                        return(d.name == 'origin') ? -20 : 251;
                    })
                    .style('fill', function(d) {
                        return hasChildNodeArr.indexOf(d) != -1 ? "#fff" : "";
                        // if (d._children || d.children) { return "#fff"; } else { return "rgba(0,0,0,0)"; }
                    })
                    .style('stroke', function(d) {
                        return hasChildNodeArr.indexOf(d) != -1 ? "#8b4513" : "";
                        // if (d._children || d.children) { return "#8b4513"; } else { return "rgba(0,0,0,0)"; }
                    })
                    .style('fill-opacity', function(d) {
                        if(d.children) {
                            return 0.35;
                        }
                    })
                    // Setting summary node style as class as mass style setting is
                    // not compatible to circles.
                    .style('stroke-width', function(d) {
                        if(d.repeated) {
                            return 5;
                        }
                    });
                //代表是否展开的+-号
                nodeEnter.append("svg:text")
                    .attr("class", "isExpand")
                    .attr("x", "251")
                    .attr("dy", function(d) {
                        return 3;
                    })
                    .attr("text-anchor", "middle")
                    .style("fill", "#000")
                    .text(function(d) {
                        if(d.name == 'origin') {
                            return '';
                        }
                        return hasChildNodeArr.indexOf(d) != -1 ? "+" : "";
                        /* if (d._children || d.children) {
                          return "+";
                        } */
                    })
                    .on('click',click);

                nodeUpdate.select('text').style('fill-opacity', 1);

                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('text')
                    .style('fill-opacity', 1e-6);

                var link = g.selectAll('path.' + link_class)
                    .data(links, function(d) {
                        return d.target.id;
                    });

                link.enter().insert('path', 'g')
                    .attr('class', link_class)
                    .attr('stroke',function(d){
                        return '#8b4513';
                    })
                    .attr('fill',"none")
                    .attr('stroke-width','1px')
                    .attr('opacity', 0.5)
                    .attr('d', function(d) {
                        var o = {
                            x: source.x0,
                            y: source.y0
                        };
                        return pathFunc({
                            source: o,
                            target: o
                        });
                    })
                    .attr("marker-end", function(d) {
                        return "url(#resolvedDown)";
                    }) //根据箭头标记的id号标记箭头;
                    .attr("id", function(d, i) {
                        return "mypath" + i;
                    });
                link.transition()
                    .duration(duration)
                    .attr('d', pathFunc);
                link.exit().transition()
                    .duration(duration)
                    .attr('d', function(d) {
                        var o = {
                            x: source.x,
                            y: source.y
                        };
                        return pathFunc({
                            source: o,
                            target: o
                        });
                    })
                    .remove();
                nodes.forEach(function(d) {
                    d.x0 = d.x;
                    d.y0 = d.y;
                });
                if(!!source.children && source.children.length>0){
                    for(let t=0;t<source.children.length;t++){
                        if(source.children[t].isExpand){
                            console.log(source)
                            // update(source.children[t], originalData, g);
                            click(source.children[t])
                        }
                    }
                }


                function get_click(d){
                    console.log(d)
                   
                }
                function Change_modal () {
                    _this.Modal = true
                }
                function click(d) {
                    console.log(d)
                    if(forUpward) {

                    } else {
                        if(d._children) {
                            console.log('对外投资--ok')
                        } else {
                            console.log('对外投资--no')
                        }
                    }
                    isExpand = !isExpand;
                    if(d.name == 'origin') {
                        return;
                    }
                    if(d.children) {
                        d._children = d.children;
                        d.children = null;
                        d3.select(this).text('+')
                    } else {
                        d.children = d._children;
                        d._children = null;
                        // expand all if it's the first node
                        if(d.name == 'origin') {
                            d.children.forEach(expand);
                        }
                        d3.select(this).text('-')
                    }
                    // console.log(d)
                    // console.log(originalData)
                    // console.log(g)
                    update(d, originalData, g);
                }
            }

            function expand(d) {
                if(d._children) {
                    d.children = d._children;
                    d.children.forEach(expand);
                    d._children = null;
                }
            }


            function collapse(d) {
                if(d.children && d.children.length != 0) {
                    d._children = d.children;
                    d._children.forEach(collapse);
                    d.children = null;
                    hasChildNodeArr.push(d);
                }
            }


            function redraw() {
                treeG.attr('transform', 'translate(' + d3.event.translate + ')' +
                    ' scale(' + d3.event.scale + ')');
            }

            function disableRightClick() {
                // stop zoom
                if(d3.event.button == 2) {
                    console.log('No right click allowed');
                    d3.event.stopImmediatePropagation();
                }
            }


            function sortByDate(a, b) {
                var aNum = a.name.substr(a.name.lastIndexOf('(') + 1, 4);
                var bNum = b.name.substr(b.name.lastIndexOf('(') + 1, 4);
                return d3.ascending(aNum, bNum) ||
                    d3.ascending(a.name, b.name) ||
                    d3.ascending(a.id, b.id);
            }
        };

        var d3GenerationChart = new treeChart(d3);
        d3GenerationChart.drawChart();

    };
    var funLine = function(obj) {  //折线
        var s = obj.source;
//			console.log('获取折线对象1:'+s.y);
        var sy = (s.name == 'origin' ? s.y+60 :s.y+260);
//			console.log('获取折线对象2:'+sy);
        var t = obj.target;
        var ty = (t.name == 'origin' ? t.y+90 :t.y+85);
        return "M"+sy+","+s.x+"L"+(sy+(ty-sy)/2)+","+s.x+"L"+(sy+(ty-sy)/2)+","+t.x+"L"+ty+","+t.x;
    }

    var downloadfun = function() { //下载

        // 注释该方法为svg 直接下载
        //  var SvgSaver = require('svgsaver');                 // if using CommonJS environment
        //  var svgsaver = new SvgSaver();                      // creates a new instance
        //  var svg = document.querySelector('#product_tree');         // find the SVG element
        //  svgsaver.asSvg(svg);                                // save as SVG

        var g = document.getElementById('product_tree').getElementsByTagName('g')[0].getBBox();
        var gbox = document.getElementById('product_tree').getElementsByClassName('gbox')[0];
        var x = -g.x;//计算偏移位置
        var y = -g.y;
        gbox.style.transform = "translate(" + x + 'px' + "," + y + "px" + ")  scale(1)"; //偏移位置
        var svgbox = $('#product_tree svg')
        var boxwidth = svgbox.width;
        var boxheight = svgbox.height;
        svgbox.attr('width', g.width)
        svgbox.attr('height', g.height)
        var canvas = document.createElement("canvas");
        var c = canvas.getContext('2d');
        //新建Image对象
        //svg内容
        var svg = document.getElementById('product_tree').innerHTML;
        var img = new Image();

        img.src = 'data:image/svg+xml,' + unescape(encodeURIComponent(svg));//svg内容中可以有中文字符
        img.src = 'data:image/svg+xml,' + svg;//svg内容中不能有中文字符
        img.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svg)));
        //图片初始化完成后调用
        img.onload = function() {
            //将canvas的宽高设置为图像的宽高
            canvas.width = img.width;
            canvas.height = img.height+60;
            c.fillStyle = "#fff";
            c.fillRect(0, 0, canvas.width, canvas.height);
            c.drawImage(img, 0, 30);
            //canvas画图片

            var a = document.createElement("a");
            a.download = "png";
            a.href= canvas.toDataURL("image/png");
            a.click();
        }
        //图片转换为base64后 传给后端 发邮件
        gbox.style.transform = ''
        // svgbox.attr('width',boxwidth)
        // svgbox.attr('height',boxheight)
        svgbox.attr('width',1100)
        svgbox.attr('height',600)

        // 结束
    }

</script>
</html>

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不能吃辣的JAVA程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值