D3.js分支图用法示例(动态鼠标可拖动节点、点击对应节点弹出对应页面、附带Echarts图表用法)

官方文档:

https://d3js.org/communityicon-default.png?t=N7T8https://d3js.org/community

效果图:

js:

initToggle() {//(2)再调这个方法
            const $this = this;
            const svgContainer = d3.select('#svg-containerbottom');
            var o = document.getElementById("svg-containerbottom");
            var height = o.offsetHeight; //高度
            var width = o.offsetWidth; //宽度
            // const width = svgContainer.style('width').replaceAll('px', '')
            // const height = svgContainer.style('height').replaceAll('px', '')
            // const svg = svgContainer.select('svg');
            let svg = svgContainer
                    .append('svg')
                    .attr("preserveAspectRatio", "xMidYMid meet")
                    .attr("viewBox", "0 0 "+width+" "+height)
                // .attr("width", width)
                // .attr("height", height)


            const toggleHeight = 200;
            const innerHeight = height - this.margin.top - this.margin.bottom;
            const innerWidth = width - this.margin.left - this.margin.right;
            let offsetY = -5;
            let g = svg.append("g")
                .attr('id', 'toggle-group')
                // .attr('transform', `translate(${this.margin.left},${this.margin.top + height / 2 - 100})`)
                .attr('transform', `translate(${this.margin.left},0)`)
                // .attr('width', innerWidth)
                // .attr('height', toggleHeight)
                .attr("preserveAspectRatio", "xMidYMid meet")
                // .attr("viewBox", "0 0 912.594 200")
            g.append("image")
                .attr('href', '../../../new/img/sbznxj/icn_go_left.svg')
                .attr('class', "img-toggle")
                .attr('x', this.margin.left)
                .attr('y', (height - 48) / 2 + offsetY)
                .attr('width', 48)
                .attr('height', 48).on('click', () => {
                this.preGroup()
            });
            g.append("image")
                .attr('href', '../../../new/img/sbznxj/icn_go_right.svg')
                .attr('class', "img-toggle")
                .attr('x', this.margin.left + innerWidth - 96)
                .attr('y', (height - 48) / 2 + offsetY)
                .attr('width', 48)
                .attr('height', 48).on("click", () => {
                this.nextGroup()
            });
            let totalSize = 0;
            for (let k = 0; k < 5; k++) {
                totalSize += 48 + k * 20;
            }
            let len = this.groups.length;
            let j = 0;
            let offsetSize = 0;
            let offsetX = (innerWidth - totalSize - 124) / 6;
            for (let i = this.groupIndex - 2; i <= this.groupIndex + 2; i++) {
                let index = (len + i) % len;
                let group = this.groups[index];
                offsetSize += 48 + j * 20;
                let size = 48 + j % (5 - j) * 20;
                let image = g.append("image")
                    .attr('href', '../../../new/img/sbznxj/icn_group.png')
                    .attr('class', "img-group")
                    .attr('id', 'img-group-id-' + j)
                    .attr('index', index)
                    .attr('x', offsetSize + offsetX * (j + 1))
                    .attr('y', (height - size) / 2 + offsetY)
                    .attr('width', size)
                    .attr('height', size)
                    .on("click", function () {
                        $this.toggle(parseInt(d3.select(this).attr("index")));
                    });
                g.append("text")
                    .attr('class', "text-group")
                    .attr('id', 'text-group-id-' + j)
                    .attr("fill", "#FFFFFF")
                    .attr("text-anchor", "middle")
                    .attr('x', parseFloat(image.attr("x")) + size / 2)
                    .attr('y', parseFloat(image.attr("y")) + size + 20)
                    .text(group.name);
                j++;
            }
        },
        initForceSimulation() {//(1)先调这个方法
            let $this = this;
            const svgContainer = d3.select('#svg-containertop')
            var o = document.getElementById("svg-containertop");
            var height = o.offsetHeight; //高度
            var width = o.offsetWidth; //宽度
            // const width = svgContainer.style('width').replaceAll('px', '')
            // const height = svgContainer.style('height').replaceAll('px', '')
            let svg;
            if (this.forceSimulation) {
                d3.select("#main-group").remove();
                svg = svgContainer.select("svg");
            } else {
                svg = svgContainer
                    .append('svg')
                    .attr("preserveAspectRatio", "xMidYMid meet")
                    .attr("viewBox", "0 0 "+width+" "+height)
                    // .attr("width", width)
                    // .attr("height", height)
            }
            const offsetY = -20;
            const innerHeight = height - this.margin.top - this.margin.bottom;
            const innerWidth = width - this.margin.left - this.margin.right;
            let g = svg.append("g")
                .attr('id', 'main-group')
                .attr('x', this.margin.left)
                .attr('y', this.margin.top)
                .attr('transform', `translate(${this.margin.left},${this.margin.top})`)
                // .attr('width', innerWidth)
                // .attr('height', innerHeight)
                .attr("preserveAspectRatio", "xMidYMid meet")
                // .attr("viewBox", "0 0 912.594 600")

            g.append("image")
                .attr('href', '../../../new/img/sbznxj/icn_circle.svg')
                .attr('x', (this.margin.left + innerWidth / 2) / 2)
                .attr('y', (this.margin.top + innerHeight / 2) / 2 + offsetY+20)
                .attr('width', innerWidth / 2)
                .attr('height', innerHeight / 2);

            this.forceSimulation = d3.forceSimulation()
                .force("link", d3.forceLink())
                .force("charge", d3.forceManyBody().strength(-1200))
                .force('collide', d3.forceCollide().radius(48).iterations(1))
                .force("center", d3.forceCenter(width / 2, height / 2))
                .force("x", d3.forceX())
                .force("y", d3.forceY());

            //生成节点数据
            this.forceSimulation.nodes(this.nodes)
                .on("tick", () => {
                    this.edges
                        .attr("x1", function (d) {
                            return d.source.x;
                        })
                        .attr("y1", function (d) {
                            return d.source.y;
                        })
                        .attr("x2", function (d) {
                            return d.target.x;
                        })
                        .attr("y2", function (d) {
                            return d.target.y;
                        });

                    this.edgeTexts
                        .attr("x", function (d) {
                            return (d.source.x + d.target.x) / 2;
                        })
                        .attr("y", function (d) {
                            return (d.source.y + d.target.y) / 2;
                        });

                    this.nodeGs
                        .attr("transform", function (d) {
                            return "translate(" + d.x + "," + d.y + ")";
                        });
                });//这个函数很重要,后面给出具体实现和说明
            //生成边数据
            this.forceSimulation.force("link")
                .links(this.links)
                .distance(function (d) {//每一边的长度
                    if ($this.groups.length >= 20) {
                        return d.value * 60;
                    } else {
                        return d.value * 80;
                    }
                })
            //设置图形的中心位置
            this.forceSimulation.force("center")
                .x(width / 2)
                .y(height / 2 + offsetY);
            //绘制边
            this.edges = g.append("g")
                .selectAll("line")
                .data(this.links)
                .enter()
                .append("line")
                .attr("stroke", "#0AE2D7")
                .attr("stroke-width", 1);
            //边上文字
            this.edgeTexts = g.append("g")
                .selectAll("text")
                .data(this.links)
                .enter()
                .append("text")
                .text(function (d) {
                    return d.relation;
                })
            //建立用来放在每个节点和对应文字的分组<g>
            this.nodeGs = g.selectAll(".circleText")
                .data(this.nodes)
                .enter()
                .append("g")
                .attr("transform", function (d, i) {
                    let cirX = d.x;
                    let cirY = d.y;
                    return "translate(" + cirX + "," + cirY + ")";
                })
                .call(this.simulation(this.forceSimulation));
            $this=this;
            //绘制节点
            this.nodeGs.append("image")
                .attr("class", "img-station")
                .attr("href", function (d, i) {
                    if (d.id === 0)
                        return "../../../new/img/sbznxj/icn_center.png";
                    else
                        return "../../../new/img/sbznxj/icn_station.svg";
                })
                .attr("width", function (d, i) {
                    if (d.id === 0)
                        return 256;
                    else
                        return 128;
                })
                .attr("height", function (d, i) {
                    if (d.id === 0)
                        return 256;
                    else
                        return 128;
                })
                .attr("transform", function (d, i) {
                    if (d.id === 0) {
                        let cirX = d.x - 128;
                        let cirY = d.y - 128;
                        return "translate(" + cirX + "," + cirY + ")";
                    } else {
                        let cirX = d.x - 64;
                        let cirY = d.y - 64;
                        return "translate(" + cirX + "," + cirY + ")";
                    }
                })
                .on('click', function (d, i) {
                   // console.log(d, i);
                    $this.viewInfoPages(i.bdzId)

                })
                .on('mouseover', function (d, i) {
                    let href = d3.select(this).attr("href");
                    if (href.endsWith(".svg")) {
                        d3.select(this).attr("href", href.replaceAll(".svg", "_hover.svg"));
                    } else if (href.endsWith(".png")) {
                        d3.select(this).attr("href", href.replaceAll(".png", "_hover.png"));
                    }
                })
                .on('mouseout', function (d, i) {
                    let href = d3.select(this).attr("href");
                    if (href.endsWith(".svg")) {
                        d3.select(this).attr("href", href.replaceAll("_hover.svg", ".svg"));
                    } else if (href.endsWith(".png")) {
                        d3.select(this).attr("href", href.replaceAll("_hover.png", ".png"));
                    }
                })
            //文字
            this.nodeGs.append("text")
                .attr("fill", "#FFFFFF")
                .attr("text-anchor", "middle")
                .attr("transform", function (d, i) {
                    let cirX = d.x;
                    let cirY = d.y + 50;
                    return "translate(" + cirX + "," + cirY + ")";
                })
                .text(function (d) {
                    if (d.id === 0)
                        return '';
                    else
                        return d.name;
                        // return d.name + d.id;
                });
        },


        simulation(simulation) {
            return d3.drag()
                .on("start", function (event, d) {
                    if (!event.active) simulation.alphaTarget(0.3).restart();
                    d.fx = d.x;
                    d.fy = d.y;
                })
                .on("drag", function (event, d) {
                    d.fx = event.x;
                    d.fy = event.y;
                })
                .on("end", function (event, d) {
                    if (!event.active) simulation.alphaTarget(0);
                    d.fx = null;
                    d.fy = null;
                });
        },
        preGroup() {
            let len = this.groups.length;
            let index = (len + (this.groupIndex - 1)) % len;
            this.toggle(index);
        },
        nextGroup() {
            let len = this.groups.length;
            let index = (this.groupIndex + 1) % len;
            this.toggle(index);
        },
        toggle(selectedIndex) {
            if (this.groupIndex !== selectedIndex) {
                this.groupIndex = selectedIndex;
                let len = this.groups.length;
                let j = 0;
                for (let i = this.groupIndex - 2; i <= this.groupIndex + 2; i++) {
                    let index = (len + i) % len;
                    let group = this.groups[index];
                    d3.select("#img-group-id-" + j).attr("index", index);
                    d3.select("#text-group-id-" + j).text(group.name);
                    j++;
                }

                this.links = [];
                this.nodes = [{
                    id: 0,
                    name: "供电公司",
                }];
                for(var i=0; i<this.groups[selectedIndex].bdz.length; i++){
                    this.links.push({
                        source: 0,
                        target: i+1,
                        value: 2,//value控制线的长短
                    })
                    this.nodes.push({
                        id: i+1,
                        name: this.groups[selectedIndex].bdz[i].NAME,
                        bdzId:this.groups[selectedIndex].bdz[i].ID,
                    })
                }
                this.initForceSimulation();
            }
        },

具体细节不用改太多东西想研究的话去看官方文档,只需要改initForceSimulation()、initToggle()方法里的图片和数据即可

再看一下调用顺序其他的不用看:

 这些是需要初始化定义的参数:

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

源码放在最下面有兴趣的小伙伴自己琢磨吧,学习记录仅供参考!,页面用的是Vue写的大屏数据可视化页面,里面有还有很多Echarts图表、点击D3js中的分支节点时可以弹出对应页面...相关功能自己甄别吧,不需要的部分自行删除

const vm = new Vue({
    el: '#app',
    data() {
        return {
            //设置当前全屏状态的初始值为 false
            fullScreen: null,
            //待巡检任务列表
            assignmentList:[],
            forceSimulation: null,
            edges: null,
            edgeTexts: null,
            nodeGs: null,
            margin: {
                top: 0,
                left: 0,
                right: 0,
                bottom: 0
            },
            links: [],//value控制线的长短
            nodes: [{
                id: 0,
                name: "供电公司",
            },],
            groups: [],
            groupIndex: 0
        };
    },
    created() {
        this.$nextTick(function(){
            // 利用屏幕分辨率和window对象的内高度来判断兼容IE
            let winFlag = window.innerHeight === window.screen.height;
            // 利用window全屏标识来判断 -- IE无效
            let isFull = window.fullScreen || document.webkitIsFullScreen;

            if (isFull === undefined) {
                this.isFullscreen = winFlag;
            } else {
                this.isFullscreen = winFlag || isFull;
            }

            if(winFlag){
                this.fullScreen=true;
            }else {
                this.fullScreen=false;
            }

        })

    },
    mounted() {
        var $this=this;
        this.getwcdChart();
        this.getpjhsChart();
        this.getfinishChart();
        this.getDxjrwlb();
        this.getbdz();

        //监听页面是否全屏
        this.$nextTick(function(){
            window.addEventListener('resize', function () {
                if (document.webkitIsFullScreen) {
                    $this.fullScreen=true;
                }else{
                    $this.fullScreen=false;
                }
            });

        })

        axios.get('/appPatrolEquipPatrolLine/treeData', {
            params: {
                //参数ID
                isline:1,
            }
        })
        .then(function (response) {	//请求成功
            console.log(response);
        })
        .catch(function (error) {		//请求失败
            console.log(error);
        });

    },
    methods: {
        //全屏按钮\退出全屏按钮点击事件
        fullScreenClick() {
            this.fullScreen = !this.fullScreen;
            if (this.fullScreen) {//当前要启动全屏
                this.WindowFullScreen();
            } else {//当前要退出全屏
                this.WindowFullScreen();
                setTimeout(() => {
                    this.windowExitFullScreen();
                }, 500);

            }
        },
        //启动全屏
        WindowFullScreen() {
            let docElm = document.getElementById('app');
            if (docElm.requestFullscreen) {
                docElm.requestFullscreen();
            }else if (docElm.msRequestFullscreen) {
                docElm.msRequestFullscreen();
            } else if (docElm.mozRequestFullScreen) {
                docElm.mozRequestFullScreen();
            }else if (docElm.webkitRequestFullScreen) {
                docElm.webkitRequestFullScreen();
            }
        },
        //退出全屏
        windowExitFullScreen() {
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.webkitCancelFullScreen) {
                document.webkitCancelFullScreen();
            }
        },


        //各维操队已完成图表
        getwcdChart() {
            var wcd=[];
            var wcddata=[];
            var sum=0;
            axios.get('/appPatrolPlanInfo/queryCompletionStatus', {
                params: {
                    //参数ID
                }
            })
            .then(function (response) {	//请求成功
                for(var i=0; i<response.data.data.length; i++){
                    wcd.push(response.data.data[i].wcd.NAME)
                    wcddata.push([response.data.data[i].wcd.NAME+' '+response.data.data[i].completedPercentage,response.data.data[i].completed])
                    sum+=response.data.data[i].completed;
                }

                var chart = Highcharts.chart('getwcdChart', {
                    chart: {
                        type: 'pie',
                        options3d: {
                            enabled: true,
                            alpha: 50
                        },
                        backgroundColor: 'rgba(255,255,255,0)',
                        marginBottom: 100,
                    },
                    exporting:{
                        enabled:false,
                    },
                    legend:{
                        align: 'right',
                        verticalAlign: 'top',
                        layout: 'vertical',
                        x: 0,
                        y: 30,
                        symbolHeight:8,
                        symbolWidth:10,
                        symbolRadius:0,
                        itemMarginBottom:10,
                        itemStyle:{
                            color:'#FFFFFF',
                            fontSize:14,
                        },
                        itemHiddenStyle:{
                            color:'#cccccc',
                        },
                        itemHoverStyle :{
                            color:'#FFFFFF',
                        },
                    },
                    credits : {
                        enabled:false//不显示highCharts版权信息
                    },
                    title: {
                        text: sum+'个',
                        margin: 0,
                        y:-10,
                        x:-70,
                        align: 'center',
                        verticalAlign: 'middle',
                        style: {
                            color: '#FFFFFF',
                            fontSize: '24px',
                            fontFamily: 'Impact',
                            right:50,
                        }
                    },
                    tooltip:{
                        distance:0,
                    },
                    plotOptions: {
                        pie: {
                            innerSize: 130,
                            depth: 30,
                            dataLabels:{
                                distance:0,
                                enabled: false,
                            },
                            showInLegend: true,
                            colors:['#00EAFF','#20E6A4','#FFCF37','#FF8100','#FC4768','#4D74FF','#1555FF','#B6BED2'],
                        }
                    },
                    series: [{
                        name: '已完成',
                        size: '110%',
                        data: wcddata,
                        dataLabels: {
                            formatter: function () {
                                return this.y < 1 ? this.point.name : null;
                            },
                            connectorWidth: 0,          // 距离值为负时显示在在扇区里面
                        },


                    }],



                });


            })
            .catch(function (error) {		//请求失败
                console.log(error);
            });


        },

        //未完成/已完成图表
        getfinishChart(){
            var $this=this;
            var categories=[];
            var ywcdata=[];
            var wwcdata=[];
            axios.get('/appPatrolPlanInfo/queryCompletionStatus', {
                params: {
                    //参数ID
                }
            })
            .then(function (response) {	//请求成功
                for(var i=0; i<response.data.data.length; i++){
                    categories.push(response.data.data[i].wcd.NAME)
                    ywcdata.push(response.data.data[i].completed)
                    wwcdata.push(response.data.data[i].incomplete)
                }
                var chart = Highcharts.chart('getfinishChart', {
                    chart: {
                        type: 'bar',
                        backgroundColor: 'rgba(255,255,255,0)',
                    },
                    exporting:{
                        enabled:false,
                    },
                    credits : {
                        enabled:false//不显示highCharts版权信息
                    },
                    title: {
                        text: null,
                    },
                    xAxis: {
                        lineWidth: 0,//隐藏x轴刻度线
                        categories: categories,//Y轴维操队名称
                        labels: {
                            style:{
                                color:'#FFFFFF',
                            },
                        },
                        min: 0,
                        max: 2,
                        scrollbar: {
                            enabled: true,
                            showFull:false,//当滚动内容缩小到最大范围时,是否显示或隐藏滚动条。
                            size:8,
                            barBorderRadius:8,
                            barBackgroundColor: '#00D8FF',
                            barBorderColor:'rgba(255,255,255,0)',
                            buttonBorderRadius:5,
                            buttonBackgroundColor:'rgba(255,255,255,0)',
                            buttonBorderColor:'rgba(255,255,255,0)',
                            rifleColor:'rgba(255,255,255,0)',
                            trackBackgroundColor: 'rgba(255,255,255,0)',
                            trackBorderColor: 'rgba(255,255,255,0)',
                        },
                    },
                    yAxis: {
                        min: 0,
                        gridLineWidth: 0,//隐藏y轴刻度线
                        title: {
                            text: null,
                        },
                        labels: {
                            style:{
                                color:'#FFFFFF',
                            },
                        },
                    },
                    legend: {
                        reversed: true,
                        itemStyle:{
                            color:'#FFFFFF',
                        },
                        itemHoverStyle :{
                            color:'#FFFFFF',
                        },
                    },
                    plotOptions: {
                        series: {
                            stacking: 'percent'
                        }
                    },
                    series: [{
                        name: '单位/个',
                        color: '#FFFFFF',
                        borderRadius: 10,
                    },{
                        name: '已完成',
                        data: ywcdata,//X轴已完成数量
                        color: {
                            linearGradient: { x1: 0, x2: 0, y1: 1, y2: 0 },
                            stops: [
                                [0, '#50EFB1'],
                                [1, '#00E0DB']
                            ]
                        },
                        pointWidth: 20,
                        borderRadius: 10,
                    },{
                        name: '未完成',
                        data: wwcdata,//X轴未完成数量
                        color: {
                            linearGradient: { x1: 0, x2: 0, y1: 1, y2: 0 },
                            stops: [
                                [0, '#FC4768'],
                                [1, '#F8D03F']
                            ]
                        },
                        pointWidth: 20,
                        borderRadius: 10,
                    }]
                });
            })
            .catch(function (error) {		//请求失败
                console.log(error);
            });


        },

        //巡检平均耗时图表
        getpjhsChart() {
            var wcd=[];
            axios.get('/appPatrolPlanInfo/getTimeSun', {
                params: {
                    //参数ID
                }
            })
            .then(function (response) {	//请求成功
                for(var item in response.data){
                    wcd.push([
                        response.data[item].LINE_NAME,
                        response.data[item].timesum/response.data[item].connum
                    ])
                }
                var chart = Highcharts.chart('getpjhsChart', {
                    chart: {
                        type: 'column',
                        backgroundColor: 'rgba(255,255,255,0)',
                    },
                    exporting:{
                        enabled:false,
                    },
                    credits : {
                        enabled:false//不显示highCharts版权信息
                    },
                    title: false,
                    subtitle: false,
                    xAxis: {
                        type: 'category',
                        labels: {
                            rotation: 60,  // 设置轴标签旋转角度
                            style:{
                                color:'#FFFFFF',
                            }
                        },
                    },
                    yAxis: {
                        min: 0,
                        title: {
                            text: false,
                        },
                        labels: {
                            style:{
                                color:'#FFFFFF',
                            }
                        },

                    },
                    legend: {
                        enabled: false
                    },
                    tooltip: {
                        pointFormat: '平均耗时: <b>{point.y:.0f} 分钟</b>'
                    },
                    series: [{
                        data: wcd,
                        dataLabels: {
                            enabled: false,
                            align: 'right',
                            format: '{point.y:.0f}', // :.1f 为保留 1 位小数
                        },
                        zones: [{
                            color: '#66FFFF'
                        }],

                    }]
                });

            })
            .catch(function (error) {		//请求失败
                console.log(error);
            });



        },

        //获取待巡检任务列表数据
        getDxjrwlb(){
            var $this=this;
            axios.get('/appPatrolPlanInfo/queryIncompletePatrolTask', {
                params: {
                    //参数ID
                }
            })
            .then(function (response) {	//请求成功
                for(var i=0; i<response.data.data.length; i++){
                    $this.assignmentList.push({
                        obj:response.data.data[i],//当前任务对象
                        code:i,//当前数组下标
                        planName:response.data.data[i].pstarttime + response.data.data[i].planName,//任务名称
                        lineIdName:response.data.data[i].tarnMap.lineIdName,//变电站名
                    },)
                }

            })
            .catch(function (error) {		//请求失败
                console.log(error);
            });


        },




        //中间图表获取变电站
        getbdz(){
            var $this=this;
            axios.get('/appPatrolPlanInfo/convertingStationList', {
                params: {
                    //参数ID
                }
            })
            .then(function (response) {	//请求成功
                for(var item in response.data.data){
                    $this.groups.push({
                        name:response.data.data[item].wcd.NAME,
                        bdz:response.data.data[item].bdz,
                    })
                    if(item==0){
                        for(var i=0; i<response.data.data[item].bdz.length; i++){
                            $this.links.push({
                                source: 0,
                                target: i+1,
                                value: 1.5,//value控制线的长短
                            })
                            $this.nodes.push({
                                id: i+1,
                                name: response.data.data[item].bdz[i].NAME,
                                // group: 2,
                                bdzId:response.data.data[item].bdz[i].ID
                            })

                        }
                    }
                }
                $this.initForceSimulation();
                $this.initToggle();
            })
            .catch(function (error) {		//请求失败
                console.log(error);
            });
        },

        initToggle() {
            const $this = this;
            const svgContainer = d3.select('#svg-containerbottom');
            var o = document.getElementById("svg-containerbottom");
            var height = o.offsetHeight; //高度
            var width = o.offsetWidth; //宽度
            // const width = svgContainer.style('width').replaceAll('px', '')
            // const height = svgContainer.style('height').replaceAll('px', '')
            // const svg = svgContainer.select('svg');
            let svg = svgContainer
                    .append('svg')
                    .attr("preserveAspectRatio", "xMidYMid meet")
                    .attr("viewBox", "0 0 "+width+" "+height)
                // .attr("width", width)
                // .attr("height", height)


            const toggleHeight = 200;
            const innerHeight = height - this.margin.top - this.margin.bottom;
            const innerWidth = width - this.margin.left - this.margin.right;
            let offsetY = -5;
            let g = svg.append("g")
                .attr('id', 'toggle-group')
                // .attr('transform', `translate(${this.margin.left},${this.margin.top + height / 2 - 100})`)
                .attr('transform', `translate(${this.margin.left},0)`)
                // .attr('width', innerWidth)
                // .attr('height', toggleHeight)
                .attr("preserveAspectRatio", "xMidYMid meet")
                // .attr("viewBox", "0 0 912.594 200")
            g.append("image")
                .attr('href', '../../../new/img/sbznxj/icn_go_left.svg')
                .attr('class', "img-toggle")
                .attr('x', this.margin.left)
                .attr('y', (height - 48) / 2 + offsetY)
                .attr('width', 48)
                .attr('height', 48).on('click', () => {
                this.preGroup()
            });
            g.append("image")
                .attr('href', '../../../new/img/sbznxj/icn_go_right.svg')
                .attr('class', "img-toggle")
                .attr('x', this.margin.left + innerWidth - 96)
                .attr('y', (height - 48) / 2 + offsetY)
                .attr('width', 48)
                .attr('height', 48).on("click", () => {
                this.nextGroup()
            });
            let totalSize = 0;
            for (let k = 0; k < 5; k++) {
                totalSize += 48 + k * 20;
            }
            let len = this.groups.length;
            let j = 0;
            let offsetSize = 0;
            let offsetX = (innerWidth - totalSize - 124) / 6;
            for (let i = this.groupIndex - 2; i <= this.groupIndex + 2; i++) {
                let index = (len + i) % len;
                let group = this.groups[index];
                offsetSize += 48 + j * 20;
                let size = 48 + j % (5 - j) * 20;
                let image = g.append("image")
                    .attr('href', '../../../new/img/sbznxj/icn_group.png')
                    .attr('class', "img-group")
                    .attr('id', 'img-group-id-' + j)
                    .attr('index', index)
                    .attr('x', offsetSize + offsetX * (j + 1))
                    .attr('y', (height - size) / 2 + offsetY)
                    .attr('width', size)
                    .attr('height', size)
                    .on("click", function () {
                        $this.toggle(parseInt(d3.select(this).attr("index")));
                    });
                g.append("text")
                    .attr('class', "text-group")
                    .attr('id', 'text-group-id-' + j)
                    .attr("fill", "#FFFFFF")
                    .attr("text-anchor", "middle")
                    .attr('x', parseFloat(image.attr("x")) + size / 2)
                    .attr('y', parseFloat(image.attr("y")) + size + 20)
                    .text(group.name);
                j++;
            }
        },
        initForceSimulation() {
            let $this = this;
            const svgContainer = d3.select('#svg-containertop')
            var o = document.getElementById("svg-containertop");
            var height = o.offsetHeight; //高度
            var width = o.offsetWidth; //宽度
            // const width = svgContainer.style('width').replaceAll('px', '')
            // const height = svgContainer.style('height').replaceAll('px', '')
            let svg;
            if (this.forceSimulation) {
                d3.select("#main-group").remove();
                svg = svgContainer.select("svg");
            } else {
                svg = svgContainer
                    .append('svg')
                    .attr("preserveAspectRatio", "xMidYMid meet")
                    .attr("viewBox", "0 0 "+width+" "+height)
                    // .attr("width", width)
                    // .attr("height", height)
            }
            const offsetY = -20;
            const innerHeight = height - this.margin.top - this.margin.bottom;
            const innerWidth = width - this.margin.left - this.margin.right;
            let g = svg.append("g")
                .attr('id', 'main-group')
                .attr('x', this.margin.left)
                .attr('y', this.margin.top)
                .attr('transform', `translate(${this.margin.left},${this.margin.top})`)
                // .attr('width', innerWidth)
                // .attr('height', innerHeight)
                .attr("preserveAspectRatio", "xMidYMid meet")
                // .attr("viewBox", "0 0 912.594 600")

            g.append("image")
                .attr('href', '../../../new/img/sbznxj/icn_circle.svg')
                .attr('x', (this.margin.left + innerWidth / 2) / 2)
                .attr('y', (this.margin.top + innerHeight / 2) / 2 + offsetY+20)
                .attr('width', innerWidth / 2)
                .attr('height', innerHeight / 2);

            this.forceSimulation = d3.forceSimulation()
                .force("link", d3.forceLink())
                .force("charge", d3.forceManyBody().strength(-1200))
                .force('collide', d3.forceCollide().radius(48).iterations(1))
                .force("center", d3.forceCenter(width / 2, height / 2))
                .force("x", d3.forceX())
                .force("y", d3.forceY());

            //生成节点数据
            this.forceSimulation.nodes(this.nodes)
                .on("tick", () => {
                    this.edges
                        .attr("x1", function (d) {
                            return d.source.x;
                        })
                        .attr("y1", function (d) {
                            return d.source.y;
                        })
                        .attr("x2", function (d) {
                            return d.target.x;
                        })
                        .attr("y2", function (d) {
                            return d.target.y;
                        });

                    this.edgeTexts
                        .attr("x", function (d) {
                            return (d.source.x + d.target.x) / 2;
                        })
                        .attr("y", function (d) {
                            return (d.source.y + d.target.y) / 2;
                        });

                    this.nodeGs
                        .attr("transform", function (d) {
                            return "translate(" + d.x + "," + d.y + ")";
                        });
                });//这个函数很重要,后面给出具体实现和说明
            //生成边数据
            this.forceSimulation.force("link")
                .links(this.links)
                .distance(function (d) {//每一边的长度
                    if ($this.groups.length >= 20) {
                        return d.value * 60;
                    } else {
                        return d.value * 80;
                    }
                })
            //设置图形的中心位置
            this.forceSimulation.force("center")
                .x(width / 2)
                .y(height / 2 + offsetY);
            //绘制边
            this.edges = g.append("g")
                .selectAll("line")
                .data(this.links)
                .enter()
                .append("line")
                .attr("stroke", "#0AE2D7")
                .attr("stroke-width", 1);
            //边上文字
            this.edgeTexts = g.append("g")
                .selectAll("text")
                .data(this.links)
                .enter()
                .append("text")
                .text(function (d) {
                    return d.relation;
                })
            //建立用来放在每个节点和对应文字的分组<g>
            this.nodeGs = g.selectAll(".circleText")
                .data(this.nodes)
                .enter()
                .append("g")
                .attr("transform", function (d, i) {
                    let cirX = d.x;
                    let cirY = d.y;
                    return "translate(" + cirX + "," + cirY + ")";
                })
                .call(this.simulation(this.forceSimulation));
            $this=this;
            //绘制节点
            this.nodeGs.append("image")
                .attr("class", "img-station")
                .attr("href", function (d, i) {
                    if (d.id === 0)
                        return "../../../new/img/sbznxj/icn_center.png";
                    else
                        return "../../../new/img/sbznxj/icn_station.svg";
                })
                .attr("width", function (d, i) {
                    if (d.id === 0)
                        return 256;
                    else
                        return 128;
                })
                .attr("height", function (d, i) {
                    if (d.id === 0)
                        return 256;
                    else
                        return 128;
                })
                .attr("transform", function (d, i) {
                    if (d.id === 0) {
                        let cirX = d.x - 128;
                        let cirY = d.y - 128;
                        return "translate(" + cirX + "," + cirY + ")";
                    } else {
                        let cirX = d.x - 64;
                        let cirY = d.y - 64;
                        return "translate(" + cirX + "," + cirY + ")";
                    }
                })
                .on('click', function (d, i) {
                   // console.log(d, i);
                    $this.viewInfoPages(i.bdzId)

                })
                .on('mouseover', function (d, i) {
                    let href = d3.select(this).attr("href");
                    if (href.endsWith(".svg")) {
                        d3.select(this).attr("href", href.replaceAll(".svg", "_hover.svg"));
                    } else if (href.endsWith(".png")) {
                        d3.select(this).attr("href", href.replaceAll(".png", "_hover.png"));
                    }
                })
                .on('mouseout', function (d, i) {
                    let href = d3.select(this).attr("href");
                    if (href.endsWith(".svg")) {
                        d3.select(this).attr("href", href.replaceAll("_hover.svg", ".svg"));
                    } else if (href.endsWith(".png")) {
                        d3.select(this).attr("href", href.replaceAll("_hover.png", ".png"));
                    }
                })
            //文字
            this.nodeGs.append("text")
                .attr("fill", "#FFFFFF")
                .attr("text-anchor", "middle")
                .attr("transform", function (d, i) {
                    let cirX = d.x;
                    let cirY = d.y + 50;
                    return "translate(" + cirX + "," + cirY + ")";
                })
                .text(function (d) {
                    if (d.id === 0)
                        return '';
                    else
                        return d.name;
                        // return d.name + d.id;
                });
        },


        simulation(simulation) {
            return d3.drag()
                .on("start", function (event, d) {
                    if (!event.active) simulation.alphaTarget(0.3).restart();
                    d.fx = d.x;
                    d.fy = d.y;
                })
                .on("drag", function (event, d) {
                    d.fx = event.x;
                    d.fy = event.y;
                })
                .on("end", function (event, d) {
                    if (!event.active) simulation.alphaTarget(0);
                    d.fx = null;
                    d.fy = null;
                });
        },
        preGroup() {
            let len = this.groups.length;
            let index = (len + (this.groupIndex - 1)) % len;
            this.toggle(index);
        },
        nextGroup() {
            let len = this.groups.length;
            let index = (this.groupIndex + 1) % len;
            this.toggle(index);
        },
        toggle(selectedIndex) {
            if (this.groupIndex !== selectedIndex) {
                this.groupIndex = selectedIndex;
                let len = this.groups.length;
                let j = 0;
                for (let i = this.groupIndex - 2; i <= this.groupIndex + 2; i++) {
                    let index = (len + i) % len;
                    let group = this.groups[index];
                    d3.select("#img-group-id-" + j).attr("index", index);
                    d3.select("#text-group-id-" + j).text(group.name);
                    j++;
                }

                this.links = [];
                this.nodes = [{
                    id: 0,
                    name: "供电公司",
                }];
                for(var i=0; i<this.groups[selectedIndex].bdz.length; i++){
                    this.links.push({
                        source: 0,
                        target: i+1,
                        value: 2,//value控制线的长短
                    })
                    this.nodes.push({
                        id: i+1,
                        name: this.groups[selectedIndex].bdz[i].NAME,
                        bdzId:this.groups[selectedIndex].bdz[i].ID,
                    })
                }
                this.initForceSimulation();
            }
        },

        //    点击中间图表图表时弹出层(这个方法是点击D3js中的分支节点时可以弹出对应页面)
        viewInfoPages(lineId){
            var obj = new Object();
            obj["lineId"] = lineId;
            obj["period"] = $("select[name=period]").val();


            showWeAdminShow('巡检任务结果','/appPatrolPlanInfo/patrolTaskInfoView', function (body, iframeWin,index) {
                layer.full(index);
                //赋值回调方法
                if (null != obj) {
                    iframeWin.setPatrolInfoIndexManageData(obj);
                }
            }, '', '');
        }

    }
})


  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

和风微凉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值