使用D3.js实现简单的图谱展示

  • 代码

<html>
	<head>
        <script src="https://d3js.org/d3.v4.min.js"></script>
        <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
        <style>
            html,body,#kgDiv{
                height: 100%;
                width: 100%;
                margin: 0;
            }
            svg circle{
                fill: white;
               
                transition: r 0.2s;
            }
            svg circle.node.active{
                r:30;
            }
            svg circle.node.centerNode{
                fill: #195ff3;
				 filter: url("#nodeShadow");
            }
            svg circle.node.centerNode.active{
                r:55;
            }
            svg .nodeImage{
               height: 20px;
               width: 20px;
            }
            svg .nodeText{
                fill:#000;
                font-size: 14px;
            }
            svg .nodeText.active{
                font-size: 20px;
            }
            svg .linkLine{
                stroke: #ccc;/*线的颜色*/
                stroke-width:1;/*线的宽度*/
            }
            svg .linkLine.active{
                stroke:#1768fd;
            }
            svg .linkTextRect{
                height: 25;
                fill: #fff;
                filter: url("#linkTextRectShadow");
                ry:5;
                rx:5;
            }
            svg .linkText{
                fill: #000;
                font-size:12px;
                fill-opacity: 0;
            }
            svg .linkText.active{
                fill-opacity: 1;
            }
            svg .linkMarker{
                fill: #ccc;
                stroke-width:2;
            }
            svg .linkMarker.active{
                fill: #1768fd;
            }
            /*滤镜svg不显示*/
            svg.filterSvg{
                height: 0;
                width: 0;
            }
            /*右键菜单的样式*/
            #contextmenu{
                display: none;
                background-color: white;
                padding: 10px 0 0 10px;
                width: 300px;
                height: 283px;
                box-shadow: 0px 0px 23px 1px rgba(0, 0, 0, 0.2);
                position: absolute; /*自定义菜单相对与body元素进行定位*/
                border-radius: 5px;
            }
            #contextmenu .contextmenuHead{
                height:21px;
                margin-right: 15px;
            }
            #contextmenu .contextmenuHead label{
                font-size:14px;
                float: left;
                color: #000;
                display: block;
            }
            #contextmenu .contextmenuHead label span{
                display: block;
                float: left;
                height: 14px;
                width: 5px;
                background-color: #195FF3;
                margin: 3px 5px 0 0;
                border-radius: 3px;
            }
            #contextmenu .contextmenuHead .operation{
                float: right;
            }
            #contextmenu .contextmenuHead .operation span{
                display: inline-block;
                margin-right: 10px;
                cursor: pointer;
            }
            #contextmenu .contextmenuHead .operation span.toview{
                height: 12px;
                width: 12px;
                background: url('./toView.png');
            }
            #contextmenu .contextmenuHead .operation span.toview:hover{
                background: url('./toView_select.png');
            }
            #contextmenu .contextmenuHead .operation span.collection{
                height: 14px;
                width: 14px;
                background: url('./collection.png');
            }
            #contextmenu .contextmenuHead .operation span.collection:hover{
                background: url('./collection_select.png');
            }
            #contextmenu .contentmenucontent{
                clear: both;
                padding:15px 10px 0 10px;
                height: 247px;
                overflow: auto;
            }
            #contextmenu .contentmenucontent div{
                line-height: 25px;
                font-size: 14px;
                color: #666;
            }
            #contextmenu .contentmenucontent div.name{
                color: #000;
                font-size: 16px;
                margin-bottom: 10px;
                font-weight: bold;
            }
			
			/*节点菜单的样式*/
			#nodeOperationMenu{
				position:absolute;
				z-index:10;
			}
			#nodeOperationMenu .ring{
				display:none;
				position:fixed;
				width:114px;
				height:114px;
				border-radius:70px;
				border: 6px solid #a4acba
			}
			#nodeOperationMenu .ring .btnList{
				list-style:none;
			}
			#nodeOperationMenu .ring .btnList li{
				position: absolute;
				box-shadow: 0 4px 5px 0 rgba(24,49,88,.2);
				width: 40px;
				height: 40px;
				color: #456;
				font-size: 12px;
				line-height: 100%;
				border: 1px solid #dfe7f5;
				border-radius: 30px;
				text-align: center;
				line-height: 38px;
				background: #fff;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(1){
				left: 37px;
				top: -26px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(2){
				left: 81.5477px;
				top: -7.54773px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(3){
				left: 100px;
				top: 37px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(4){
				left: 81.5477px;
				top: 81.5477px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(5){
				left: 37px;
				top: 100px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(6){
				left: -7.54773px;
				top: 81.5477px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(7){
				left: -26px;
				top: 37px;
			}
			#nodeOperationMenu .ring .btnList li:nth-child(8){
				left: -7.54773px;
				top: -7.54773px;
			}
        </style>
    </head>
    <body>
        <div id="kgDiv">
            
        </div>
        <svg class="filterSvg">
            <defs>
                <filter id="linkTextRectShadow" y="-10" height="40" x="-10" width="150">
                    <feOffset in="SourceGraphic" dx="1" dy="1" result="offset2" />
                    <feColorMatrix type="matrix" in="offset2" result="matrixOut" 
                        values="0.81 0 0 0 0 
                                0 0.87 0 0 0 
                                0 0 0.98 0 0 
                                0 0 0 1 0">
                    </feColorMatrix>
                    <feGaussianBlur in="matrixOut" stdDeviation="3"  result="blur2"/>
                    <feMerge>
                        <feMergeNode in="blur2" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <filter id="nodeShadow" y="-50" height="150" x="-50" width="150">
                        <feOffset in="SourceGraphic" dx="0" dy="0" result="offset2" />
                        <feColorMatrix type="matrix" in="offset2" result="matrixOut" 
                            values="0.54 0 0 0 0 
                                    0 0.68 0 0 0 
                                    0 0 0.97 0 0 
                                    0 0 0 1 0">
                        </feColorMatrix>
                        <feGaussianBlur in="matrixOut" stdDeviation="6"  result="blur2"/>
                        <feMerge>
                            <feMergeNode in="blur2" />
                            <feMergeNode in="SourceGraphic" />
                        </feMerge>
                    </filter>
            </defs>
        </svg>
        <div id="contextmenu">
            <!-- <div class="contextmenuHead">
                <label>
                    <span></span>
                    基本信息
                </label>
                <div class="operation">
                    <span title="查看简历" class="toview" id="toViewResume"></span>
                    <span title="收藏简历" class="collection" id="collectResume"></span>
                </div>
            </div>
            <div class='contentmenucontent'>
                <div class="name">邵先生</div>
                <div><span>出生年月:</span></div>
                <div><span>性别:</span></div>
                <div><span>年龄:</span></div>
                <div><span>现居:</span></div>
                <div><span>教育经历:</span></div>
                <div><span>工作经历:</span></div>
            </div> -->
        </div>
		
		<!--节点菜单-->
		<div id="nodeOperationMenu">
			<div class="ring">
				<ul class="btnList">
					<li>切换</li>
					<li>备注</li>
					<li>关注</li>
					<li>扩展</li>
					<li>探寻</li>
					<li>路径</li>
					<li>隐藏</li>
					<li>详情</li>
				<div>
			</div>
		</div>
		
    </body>
    <script>
            //数据
            var nodes = [{id:1,name:'小黄',img:'./icon1.png'},
                        {id:2,name:'小红',img:'./icon_lock.png'},
                        {id:3,name:'小蓝',img:'./icon_2.png'},
                        {id:4,name:'小绿',img:'./icon_3.png'}];
            var links = [{source:1,target:2,relation:'朋友'},{source:1,target:3,relation:'老师历史老师老师'},{source:1,target:4,relation:'学生'}];
            
            
            var force;
            var linkLine;
            var linkText;
            var linkTextRect;
            var linkMarker;
            //tick表示运动进行中每更新一帧时
            function ticked(){
                //更新连接线的位置
                linkLine.attr('d',function(d){
                    var path = 'M ' + d.source.x + ' ' + d.source.y 
                        + ' L ' + d.target.x + ' ' + d.target.y;
                    return path;
                });
                //更新连接线上的矩形的位置
                linkTextRect.attr('x',function(d){
                    return (d.source.x + d.target.x) / 2 - 10;
                }).attr('y',function(d){
                    return (d.source.y + d.target.y) / 2 - 16.5;
                }).style('width',function(d){
                    return d.relation.length * 12 + 20;
                });
                //更新连接线上的文字的位置
                linkText.attr('x',function(d){
                    return (d.source.x + d.target.x) / 2;
                }).attr('y',function(d){
                    return (d.source.y + d.target.y) / 2;
                });
                //更新节点和文字
                d3.selectAll('circle')
                    .attr('cx',function(d){
                        return d.x
                    })
                    .attr('cy',function(d){return d.y});
                d3.selectAll('.nodeText').attr('x',function(d){return d.x}).attr('y',function(d){if(d.id === 1){return d.y + 75} return d.y + 50});
                d3.selectAll('.nodeImage').attr('x',function(d){return d.x - 10}).attr('y',function(d){return d.y - 10});
            }
            //鼠标悬浮到节点、节点名称、节点图片上时 节点、节点名称、连线的样式随之变换
            function mouseover(self,d){
                d3.select(self.parentNode).select('.node').classed('active',true);
                d3.select(self.parentNode).select('text').classed('active',true);
                linkLine.classed("active",function(link){
                    if(link.source === d || link.target === d){
                        return true;
                    }else{
                        return false;
                    }
                });
                linkMarker.classed('active',function(link){
                    if(link.source === d || link.target === d){
                        return true;
                    }else{
                        return false;
                    }
                });
            }
            //鼠标从节点、节点名称、节点图片上移开时 节点、节点名称、连线的样式还原
            function mouseout(self){
                d3.select(self.parentNode).select('.node').classed('active',false);
                d3.selectAll('.nodeText').classed('active',false);
                linkLine.classed('active',false);
                linkMarker.classed('active',false);
            }
            //右键节点、节点名称、节点图片显示菜单
            function contextmenu(self,d){
                mouseover(self,d);
                //停止布局
                force.stop();
                /*var menu = document.querySelector("#contextmenu");
                menu.style.left = d3.event.clientX + 'px';
                menu.style.top = d3.event.clientY +'px';
                //显示右键菜单
                document.querySelector('#contextmenu').style.display='block';
                document.querySelector('#contextmenu').innerHTML = '<div class="contextmenuHead"><label><span></span>基本信息</label><div class="operation"><span title="查看简历" class="toview" id="toViewResume"></span><span title="收藏简历" class="collection" id="collectResume"></span></div></div><div class="contentmenucontent"><div class="name">'+ d.name +'</div><div><span>出生年月:</span></div><div><span>性别:</span></div><div><span>年龄:</span></div><div><span>现居:</span></div><div><span>教育经历:</span></div><div><span>工作经历:</span></div></div>';
                document.getElementById("toViewResume").onclick = function (ev) {console.log("查看简历");};
                document.getElementById("collectResume").onclick = function (ev) {console.log("收藏简历")};    */
				var e = d3.event;
				var x = $(d3.event.target).offset();
				console.log(d3.event,x,"d3.event");
				//显示右键菜单
                //document.querySelector('#nodeOperationMenu > .ring').style.display = 'block';
				//document.querySelector('#nodeOperationMenu > .ring').style.top = x.top + 25 + 25 - 63;
				//document.querySelector('#nodeOperationMenu > .ring').style.left = x.left + 25 + 25 - 63;
            }
            //双击节点 以该节点为中心重新生成图谱
            function dblclickNode(d){
                //双击中心节点无操作。
                if(d.id == 1){
                    return;
                }
                //停止布局
                force.stop();
                var nodes = [{id:1,name:d.name,img:d.img},
                            {id:5,name:'小花',img:'./icon_lock.png'},
                            {id:6,name:'小草',img:'./icon_2.png'},
                            {id:7,name:'小衰',img:'./icon_3.png'}];
                var links = [{source:1,target:5,relation:'朋友'},{source:1,target:7,relation:'历史老师'},{source:1,target:6,relation:'学生'}];
                kgBuider(nodes,links);
            }
            //生成图谱
            function kgBuider(nodes,links){
                //生成图谱之前先清空之前的图谱
                document.getElementById('kgDiv').textContent = '';
                var scope = document.getElementById('kgDiv');
                scope.oncontextmenu = function (e) {
                    e.preventDefault();
                };
                scope.onclick = function (e) {
                    //关闭右键菜单
                    document.querySelector('#contextmenu').style.display='none';
                };
                //图谱的宽高
                var height = document.body.clientHeight;
                var width = document.body.clientWidth;
                //设置画布
                var svg = d3.select("#kgDiv").append("svg")
                    .style("height","100%").style("width","100%");
                //设置力导向图
                force = d3.forceSimulation()
                    //设置画板中心点
                    .force('center',d3.forceCenter(width/2,height/2))
                    //设置节点间的作用力
                    .force('charge',d3.forceManyBody().strength(-200))
                    .on('tick',ticked)
                    //设置node和link
                    .nodes(nodes)
                    .force('link',d3.forceLink(links).id(function(d){return d.id}).distance(211).strength(0.2))
                    .restart();;
                //设置缩放,svg嵌套g标签,缩放在g标签上进行
                var g = svg.append('g');
                var zoomObj = d3.zoom()
                    //监听zoom事件,缩放时调用
                    .on('zoom',function(){
                        var transform = d3.event.transform;
                        g.attr('transform',transform);
                    })
                    //监听end事件,缩放结束时调用
                    .on('end',function(){
                        var transform = d3.event.transform;
                        g.attr('transform',transform);
                    });
                svg.call(zoomObj).on("dblclick.zoom", null);//取消双击缩放
                var relationship = g.append('g').attr('class','relationships').selectAll('.relationship').data(links).enter().append('g').attr('class','relationship');
                //设置连线上的箭头
                linkMarker = relationship.append('marker')
                    .attr('class','linkMarker')
                    .attr('id',function(d,i){
                        return 'marker_' + i;
                    })
                    .attr('markerUnits','userSpaceOnUse')//设置为strokeWidth箭头会随着线的粗细发生变化,userSpaceOnUse用于确定marker是否进行缩放。取值strokeWidth和userSpaceOnUse,
                    .attr('viewBox','0 -5 10 10')
                    .attr('refX',30)//箭头坐标
                    .attr('refY',0)
                    .attr('markerWidth',12)//标识的大小
                    .attr('markerHeight',12)
                    .attr('orient','auto')//绘制方向 可设定为:auto(自动确认方向)或者 角度值
                //设置箭头的路径
                linkMarker.append('path')
                    .attr('d','M0,-5L10,0L0,5');//箭头的路径 
                //设置连线
                linkLine = relationship
                    .append('path')
                    .attr('class','linkLine')
                    //设置线的末尾为箭头
                    .attr('marker-end',function(d,i){
                        return 'url(#marker_' + i + ')';
                    })
                //设置一个矩形,将文本放到其中
                linkTextRect = relationship
                    .append('rect')
                    .attr('class','linkTextRect')   
                //设置连接线上的文本
                linkText = relationship
                    .append('text')
                    .attr('class','linkText active')
                    .text(function(d){
                        //连线文字为节点之间的关系
                        return d.relation;
                    })
                //设置拖拽
                var drag = d3.drag()
                    .on('start',function(d,i){
                        if(!d3.event.active){
                            //拖拽开始回调
                            force.alphaTarget(0.3).restart();//可以使用在交互时重新启动仿真,重新进行布局。必须设置,不然拖动不了。
                        }
                        d.fixed = false;//偏移后是否固定不动
                        d.fx = d.x;//记录当前默认位置(x - 节点当前的 x-位置,如果要为某个节点设置默认的位置,则需要为该节点设置如下两个属性:fx =x位置)
                        d.fy = d.y;
                    })
                    .on('drag',function(d,i){
                        d.fx = d3.event.x;
                        d.fy = d3.event.y;
                    })
                    .on('end',function(d,i){
                        //拖动结束后
                        if(!d3.event.active){
                            force.alphaTarget(0);
                        }
                        d.fx = null;
                        d.fy = null;
                    })
                
                var node = g.append('g').attr('class','nodeGroup').selectAll('.node').data(nodes).enter().append('g');
                //设置节点
                node.append('circle')
                    .attr('class',function(d,i){
                        if(d.id === 1){
                            return 'node centerNode';
                        }else{
                            return 'node';
                        }
                    })
                    .attr('r',function(d,i){
                        if(d.id === 1){
                            return 50;
                        }else{
                            return 25;
                        }
                    })
					.style('fill',function(d){
						if(d.name == '小黄'){
							return 'yellow';
						}else if(d.name == '小红'){
							return 'red';
						}else if(d.name == '小蓝'){
							return 'blue';
						}else if(d.name == '小绿'){
							return 'green';
						}
					})
                    //.on('mouseover',function(d){mouseover(this,d); /*d3.select(this).raise()*/})
                    //.on("mouseout",function(d){mouseout(this)})
                    .on('contextmenu',function(d){contextmenu(this,d);})
                    //.on('dblclick',function(d){dblclickNode(d);})
                    .call(drag);
                //设置节点文字
                node.append('text').attr('text-anchor','middle')
                    .attr('class', 'nodeText')
                    .text(function (d,i) { return d.name })
                    //.on('mouseover', function(d){mouseover(this,d); /*d3.select(this).raise()*/})
                    //.on('mouseout', function(d){mouseout(this)})
                    .on('contextmenu',function(d){contextmenu(this,d);})
                    //.on('dblclick',function(d){dblclickNode(d);})
                    .call(drag);
                //设置节点图片
                node.append('image').attr('class','nodeImage')
                    .attr('href',function(d,i){
                        return d.img;
                    })
                    //.on('mouseover', function(d){mouseover(this,d); /*d3.select(this).raise()*/})
                    //.on('mouseout', function(d){mouseout(this)})
                    .on('contextmenu',function(d){contextmenu(this,d);})
                    //.on('dblclick',function(d){dblclickNode(d);})
                    .call(drag);
            }
            kgBuider(nodes,links);
			
			$(document).ready(function(){
				$('svg *').mousedown(function(e){
				if(e.button != 2){ document.querySelector('#nodeOperationMenu > .ring').style.display = 'none';}
				  
			})
			})
        </script>
</html>
  • 效果图

效果图

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
d3.js是一个基于数据操作文档的JavaScript库,可以帮助开发者使用HTML、SVG和CSS来展示数据。d3.js可以用来实现人物关系图谱,具体实现步骤如下: ```javascript // 引入d3.js库 <script src="https://d3js.org/d3.v3.min.js"></script> // 创建svg元素 var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); // 创建力导向图 var force = d3.layout.force() .nodes(nodes) // 节点数组 .links(links) // 连线数组 .size([width, height]) // 画布大小 .linkDistance(150) // 连线长度 .charge(-400); // 节点间的斥力 // 绘制连线 var links = svg.selectAll(".link") .data(links) .enter() .append("line") .attr("class", "link"); // 绘制节点 var nodes = svg.selectAll(".node") .data(nodes) .enter() .append("circle") .attr("class", "node") .attr("r", 20) .style("fill", function(d) { return color(d.group); }) .call(force.drag); // 节点可拖拽 // 绘制节点标签 var labels = svg.selectAll(".label") .data(nodes) .enter() .append("text") .attr("class", "label") .attr("fill", "black") .attr("font-size", "12px") .attr("dx", 20) .attr("dy", 8) .text(function(d) { return d.name; }); // 开始力导向布局 force.start(); // 监听力导向图布局事件 force.on("tick", function() { // 更新连线位置 links.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; }); // 更新节点位置 nodes.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); // 更新节点标签位置 labels.attr("x", function(d) { return d.x; }) .attr("y", function(d) { return d.y; }); }); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值