123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- <style>
- .link-active {
- stroke-opacity: 1;
- stroke-width: 3;
- }
- </style>
- <script src="http://d3js.org/d3.v5.min.js"></script>
- </head>
- <body>
- <div style="border:1px solid #000;position: relative;">
- <svg width="1800" height="890"></svg>
- </div>
- </body>
- <script>
- let marge = { top: 60, bottom: 60, left: 60, right: 60 }
- let svg = d3.select('svg')
- let width = svg.attr('width')
- let height = svg.attr('height')
- svg.call(
- d3.zoom().on('zoom', function () {
- g.attr('transform', d3.event.transform)
- })
- )
- .on('dblclick.zoom', null)
- let g = svg.append('g')
- .attr('transform', 'translate(' + marge.top + ',' + marge.left + ')')
- .attr('class', 'container')
- // 准备数据
- // 节点集
- let nodes = [
- { id: 12, name: '湖南邵阳' },
- { id: 2, name: '山东泰安' },
- { id: 3, name: '广东阳江不知道怎么回' },
- { id: 4, name: '山西太原' },
- { id: 5, name: '亮' },
- { id: 6, name: '丽' },
- { id: 7, name: '雪' },
- { id: 8, name: '小明' },
- { id: 9, name: '组长' }
- ]
- // 143 112
- // 边集
- let tempEdges = [
- { id: 1, source: 12, target: 5, relation: '籍贯', value: 1.3 },
- { id: 2, source: 5, target: 6, relation: '舍友', value: 1 },
- { id: 3, source: 5, target: 7, relation: '舍友', value: 1 },
- { id: 4, source: 5, target: 8, relation: '舍友', value: 1 },
- { id: 5, source: 2, target: 7, relation: '籍贯', value: 2 },
- { id: 6, source: 3, target: 6, relation: '籍贯', value: 0.9 },
- { id: 7, source: 4, target: 8, relation: '籍贯', value: 1 },
- { id: 8, source: 6, target: 7, relation: '同学', value: 1.6 },
- { id: 9, source: 7, target: 8, relation: '朋友', value: 0.7 },
- { id: 10, source: 7, target: 9, relation: '职责', value: 2 },
- { id: 11, source: 9, target: 7, relation: '人物', value: 2 },
- { id: 12, source: 9, target: 7, relation: '哈哈哈', value: 2 }
- ]
- nodes.forEach(item => {
- })
- // 生成 nodes map
- let nodesMap = genNodesMap(nodes);
- console.log('3333',nodesMap)
- nodesData = d3.values(nodesMap)
- let linkMap = genLinkMap(tempEdges)
- // 构建 links(source 属性必须从 0 开始)
- edges = genLinks(tempEdges);
- console.log('123123',edges,nodesData)
- // 设置一个颜色比例尺
- let colorScale = d3.scaleOrdinal()
- .domain(d3.range(nodesData.length))
- .range(d3.schemeCategory10)
- // 新建一个力导向图
- let forceSimulation = d3.forceSimulation()
- .force('link', d3.forceLink())
- .force('charge', d3.forceManyBody())
- .force('center', d3.forceCenter())
- // 生成节点数据
- forceSimulation.nodes(nodesData)
- // 生成边数据
- forceSimulation.force('link')
- .links(edges)
- .distance(function (d) { // 每一边的长度
- return d.value * 200
- })
- // 设置图形中心位置
- forceSimulation.force('center')
- .x(width / 2)
- .y(height / 2)
- // 箭头
- var marker = g.append('g').attr('class', 'showLine').append('marker')
- .attr('id', 'resolved')
- // .attr("markerUnits","strokeWidth")// 设置为strokeWidth箭头会随着线的粗细发生变化
- .attr('markerUnits', 'userSpaceOnUse')
- .attr('viewBox', '0 -5 10 10')// 坐标系的区域
- .attr('refX', 32)// 箭头坐标
- .attr('refY', 0)
- .attr('markerWidth', 10)// 标识的大小
- .attr('markerHeight', 10)
- .attr('orient', 'auto')// 绘制方向,可设定为:auto(自动确认方向)和 角度值
- .attr('stroke-width', 2)// 箭头宽度
- .append('path')
- .attr('d', 'M0,-5L10,0L0,5')// 箭头的路径
- .attr('fill', '#4ffeff')// 箭头颜色
- // 绘制边
- let links = g.append('g').selectAll('path')
- .data(edges)
- .enter()
- .append('path')
- .attr('d', link => genLinkPath(link)) // 遍历所有数据。d表示当前遍历到的数据,返回绘制的贝塞尔曲线
- .attr('id', (d, i) => { return 'edgepath' + d.id }) // 设置id,用于连线文字
- .attr("stroke", "#4ffeff")
- .attr("stroke-opacity", 0.6)
- .style('stroke-width', 1) // 粗细
- .attr('class', 'lines')
- .attr('marker-end', 'url(#resolved)') // 根据箭头标记的id号标记箭头
- // 边上的文字
- let linksText = g.append('g')
- .selectAll('text')
- .data(edges)
- .enter()
- .append('text')
- .style("fill", "#C0F8F8")
- .attr('class', 'linksText')
- .text(function (d) {
- return d.relations
- })
- .style('font-size', 14)
- //图片容器
- let pattern = g.append('g')
- .selectAll("pattern")
- .data(nodesData, function (node) {
-
- return "pattern" + node.index;
- })
- .join("pattern")
- .attr("id", function (node) {
- return "pattern" + node.index;
- })
- .attr("x", 0)
- .attr("y", 0)
- .attr("height", 10)
- .attr("width", 10)
- .append("svg:image");
- console.log(pattern);
- //节点背景图片地址
- let images = g
- .selectAll("image")
- .data(nodesData, function (node) {
- console.log(node);
- return "pattern-image" + node.index;
- })
- .attr("xlink:href", function (node) {
-
- return 'data:image/png;base64,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'
- })
- .attr("preserveAspectRatio", "none")
- .attr("x", 0)
- .attr("y", 0)
- .attr("height", function (node) {
- return 70;
- })
- .attr("width", function (node) {
- return 70;
- });
- // 创建分组
- let gs = g.append('g')
- .selectAll('.circleText')
- .data(nodesData)
- .enter()
- .append('g')
- .attr('class', 'singleNode')
- .attr('id', function (d) {
- return 'singleNode' + d.id
- })
- .style('cursor', 'pointer')
- .attr('transform', function (d) {
- let cirX = d.x
- let cirY = d.y
- return 'translate(' + cirX + ',' + cirY + ')'
- })
-
- // 鼠标交互
- // gs.on('mouseover', function (d, i) {
- // // 显示连接线上的文字
- // toggleLineText(d, true)
- // toggleLine(links, d, true)
- // toggleNode(gs, d, true)
- // })
- // .on('mouseout', function (d, i) {
- // // 隐去连接线上的文字
- // toggleLineText(d, false)
- // toggleLine(links, d, false)
- // toggleNode(gs, d, false)
- // })
- .on('click', function (d, i) {
- linksText.style('fill-opacity', function (edge) {
- if (edge.source === d) {
- return 1
- }
- })
- console.log(d);
- toggleCircle(d3.select(this), d)
- }, true)
- .call(d3.drag()
- .on('start', started)
- .on('drag', dragged)
- .on('end', ended)
- )
- svg.on('click', function () {
- nodes.forEach(d => d.clickFlag = false)
- var event = d3.event
- var target = event.srcElement, // 获取事件发生源
- data = d3.select(target).datum(); // 获取事件发生源的数据
- removeSingle()
- if (!data) {
- document.getElementById('xxx').innerText = ''
- }
- }, true)
- forceSimulation.on('tick', ticked)
- function toggleLineText(currNode, isHover) {
- if (isHover) {
- linksText.style('fill-opacity', function (edge) {
- if (edge.source === currNode) {
- return 1
- }
- })
- } else {
- linksText.style('fill-opacity', function (edge) {
- if (edge.source === currNode || edge.target === currNode) {
- return 0
- }
- })
- }
- }
- function toggleLine(linkLine, currNode, isHover) {
- if (isHover) {
- // 加重连线样式
- links
- .style('opacity', 0.1)
- .filter(link => isLinkLine(currNode, link))
- .style('opacity', 1)
- .classed('link-active', true)
- } else {
- links
- .style('opacity', 1)
- .classed('link-active', false)
- }
- }
- function showMyList() {
- var e = { id: 10, name: "河北" };
- var h = { id: 11, name: "河南" };
- var f = { id: 13, source: 9, target: 10, relation: '222', value: 2 };
- nodes.push(e);
- nodes.push(h);
- tempEdges.push(f);
- nodesMap = genNodesMap(nodes);
- nodesData = d3.values(nodesMap)
- linkMap = genLinkMap(tempEdges)
- edges = genLinks(tempEdges)
- updateData()
- }
- function updateData() {
- links = links
- .data(edges, function (d) {
- })
- .join("path")
- .attr('id', (d, i) => { return 'edgepath' + d.id }) // 设置id,用于连线文字
- .style('stroke', '#000') // 颜色
- .style('stroke-width', 2) // 粗细
- .attr('class', 'lines')
- .attr('marker-end', 'url(#resolved)') // 根据箭头标记的id号标记箭头
- .merge(links);
- linksText = linksText
- .data(edges)
-
- .join('text')
- .attr('class', 'linksText')
- .text(function (d) {
- return d.relations
- })
- .style('font-size', 14)
- .attr('fill-opacity', 0)
- gs = gs
- .data(nodesData, function (d) {
- })
- .join("g")
- .attr('class', 'singleNode')
- .attr('id', function (d) {
- return 'singleNode' + d.id
- })
- .style('cursor', 'pointer')
- .merge(gs)
- .call(d3.drag()
- .on("start", started)
- .on("drag", dragged)
- .on("end", ended));
- gs.append('circle')
- .attr('fill', function (d) {
- return 'orange'
- })
- .attr('r', 35)
- .attr('stroke', 'red')
- .attr('stroke-width', 3)
- // gs.on('mouseover', function (d, i) {
- // // 显示连接线上的文字
- // toggleLineText(d, true)
- // toggleLine(links, d, true)
- // toggleNode(gs, d, true)
- // })
- // .on('mouseout', function (d, i) {
- // // 隐去连接线上的文字
- // toggleLineText(d, false)
- // toggleLine(links, d, false)
- // toggleNode(gs, d, false)
- // })
- .on('click', function (d, i) {
- linksText.style('fill-opacity', function (edge) {
- if (edge.source === d) {
- return 1
- }
- })
- toggleCircle(d3.select(this), d)
- }, true)
- gs.append('text')
- .attr('y', -20)
- .attr('dy', 10)
- .attr('text-anchor', 'middle')
- .style('font-size', 12)
- .attr('x', function ({ name }) {
- return textBreaking(d3.select(this), name)
- })
- gs.append('title')
- .text((node) => {
- return node.name
- })
- forceSimulation.nodes(nodesData);
- forceSimulation.force("link").links(edges);
- forceSimulation.alpha(0.8).restart();
- }
- function getLineTextAngle(d, bbox) {
- if (d.target.x < d.source.x) {
- const {
- x,
- y,
- width,
- height
- } = bbox;
- const rx = x + width / 2;
- const ry = y + height / 2;
- return 'rotate(180 ' + rx + ' ' + ry + ')';
- } else {
- return 'rotate(0)';
- }
- }
- function toggleNode(nodeCircle, currNode, isHover) {
- if (isHover) {
- // 提升节点层级
- // nodeCircle.sort((a, b) => a.id === currNode.id ? 1 : -1);
- // this.parentNode.appendChild(this);
- nodeCircle
- .style('opacity', .1)
- .filter(node => isLinkNode(currNode, node))
- .style('opacity', 1);
- } else {
- nodeCircle.style('opacity', 1);
- }
- }
- //删除有用
- function removeSingle() {
- d3.select('.singleCircle').remove()
- }
- function toggleCircle(current, d) {
- var currentD = d
- // if (d.clickFlag) {
- // removeSingle()
- // document.getElementById('xxx').innerText = ''
- // }
- d.clickFlag = true
- // document.getElementById('xxx').innerText = d.name
- var data = [{
- population: 30,
- value: '删除',
- type: 'delete'
- }, {
- population: 30,
- value: '收起',
- type: 'showOn'
- }, {
- population: 30,
- value: '展开',
- type: 'showOff'
- }]
- var sum = d3.sum(data.map(function (d) {
- return d.population
- }))
- for (i in data) {
- data[i].Percentage = (data[i].population / sum * 100).toFixed(0) + "%";
- }
- var width = 300,
- height = 300,
- margin = { "left": 30, "top": 30, "right": 30, "bottom": 30 },
- svg_width = width + margin.left + margin.right,
- svg_height = height + margin.top + margin.bottom,
- font_size = 15;
- var g = current
- .append("g")
- .attr('class', 'singleCircle')
- .attr("width", width)
- .attr("height", height)
- console.log(g);
- var Pie = g.append("g")
- console.log(Pie);
- var arc_generator = d3.arc()
- .innerRadius(width / 6.5)
- .outerRadius(width / 4)
- var angle_data = d3.pie()
- .value(function (d) {
- return d.population;
- })
- var pieData = angle_data(data)
- var pieAngle = pieData.map(function (p) {
- return (p.startAngle + p.endAngle) / 2 / Math.PI * 180;
- });
- // var color=d3.schemeCategory10;
- //生成内部圆环
- Pie.selectAll("path")
- .data(angle_data(data))
- .enter()
- .append("path")
- .attr("d", arc_generator)
- .style("fill", function (d, i) {
- return 'grey';
- })
- .style('stroke', 'black')
- .attr("class", "path")
- .attr('type', function (d) {
- return d.data.type
- })
- .on('click', function (d) {
- if (d.data.type === 'delete') {
- deleteNode(currentD)
- } else if (d.data.type === 'showOn') {
- deleteNextNodes(currentD)
- } else {
- // showMyList()
- }
- d3.event.stopPropagation()
- })
- var arc_label = d3.arc()
- .innerRadius(width / 4)
- .outerRadius(width / 2)
- Pie.selectAll(".arc_label")
- .data(angle_data(data))
- .enter()
- .append("path")
- .attr("d", arc_label)
- .attr("class", "arc_label")
- .style("fill", "none")
- const labelFontSize = 12;
- const labelValRadius = (170 * 0.35 - labelFontSize * 0.35); // 计算正确半径 文字位置
- const labelValRadius1 = (170 * 0.35 + labelFontSize * 0.35);
- const labelsVals = current.select('.singleCircle').append('g')
- .classed('labelsvals', true);
- // 定义两条路径以使标签的方向正确
- labelsVals.append('def')
- .append('path')
- .attr('id', 'label-path-1')
- .attr('d', `m0 ${-labelValRadius} a${labelValRadius} ${labelValRadius} 0 1,1 -0.01 0`);
- labelsVals.append('def')
- .append('path')
- .attr('id', 'label-path-2')
- .attr('d', `m0 ${-labelValRadius1} a${labelValRadius1} ${labelValRadius1} 0 1,0 0.01 0`);
- labelsVals.selectAll('text')
- .data(data)
- .enter()
- .append('text')
- .style('font-size', labelFontSize)
- .style('fill', 'black')
- .style('font-weight', "bold")
- .style('text-anchor', 'middle')
- .append('textPath')
- .attr('href', function (d, i) {
- const p = pieData[i];
- const angle = pieAngle[i];
- if (angle > 90 && angle <= 270) { // 根据角度选择路径
- return '#label-path-2';
- } else {
- return '#label-path-1';
- }
- })
- .attr('startOffset', function (d, i) {
- const p = pieData[i];
- const angle = pieAngle[i];
- let percent = (p.startAngle + p.endAngle) / 2 / 2 / Math.PI * 100;
- if (angle > 90 && angle <= 270) { // 分别计算每条路径的正确百分比
- return 100 - percent + "%";
- }
- return percent + "%";
- })
- .text(function (d) {
- return d.value;
- })
- .on('click', function (d) {
- if (d.type === 'delete') {
- deleteNode(currentD)
- } else if (d.type === 'showOn') {
- deleteNextNodes(currentD)
- } else {
- // showMyList()
- }
- d3.event.stopPropagation()
- }, true)
- }
- //删除当前节点下一级没有其他关系的节点
- function deleteNextNodes(curr) {
- console.log('收起?');
- // document.getElementById('xxx').innerText = '';
- // var removeIndex = nodesData.findIndex(node=>node.id == curr.id)
- // nodesData.splice(removeIndex,1)
- // nodes.splice(removeIndex,1)
- d3.select(this).remove();
- let relationNode = [],
- relationList = [],
- hasRelationList = []
- var clickNode = curr.id;//点击节点的名字
- d3.selectAll(".lines").each(function (e) {
- if (e.source.id == curr.id || e.target.id == curr.id) {
- hasRelationList.push(e)
- } else {
- relationList.push(e)//出去跟删除节点有关系的其他关系
- }
- //需要删除的节点相关的节点
- if (e.source.id == curr.id) {
- relationNode.push(e.target)
- }
- //需要删除的节点相关的节点
- if (e.target.id == curr.id) {
- relationNode.push(e.source)
- }
- })
- var tempNodeList = JSON.parse(JSON.stringify(relationNode))
- tempNodeList = uniqObjInArray(tempNodeList)
- //区分下级节点是否是孤节点
- tempNodeList.forEach(function (item, index) {
- var hasLine = relationList.findIndex(jtem => jtem.target.id == item.id || jtem.source.id == item.id)
- if (hasLine >= 0) {
- item.notSingle = true
- }
- })
- tempNodeList.forEach(function (item, index) {
- if (!item.notSingle) {
- d3.select('#singleNode' + item.id).remove()
- }
- })
- var otherTempNode = [];
- tempNodeList = tempNodeList.map(item => {
- if (!item.notSingle) {
- otherTempNode.push(item)
- }
- })
- hasRelationList.forEach(item => {
- otherTempNode.forEach(jtem => {
- if (jtem.id == item.source.id || jtem.id == item.target.id) {
- d3.select('#edgepath' + item.id).remove()
- }
- })
- })
- d3.selectAll(".singleNode").each(function (d, i) {
- var temp = d.id;
- //删除当前需要隐藏的节点
- if (temp == clickNode) {
- removeSingle()
- }
- });
- d3.selectAll(".linksText").each(function (e) {
- if (e.source === curr || e.target === curr) {
- d3.select(this).remove();
- }
- })
- gs.style('opacity', 1);
- links.style('opacity', 1)
- .classed('link-active', false);
- }
- //删除当前及下一级没有其他关系的节点
- function deleteNode(curr) {
- document.getElementById('xxx').innerText = '';
- var removeIndex = nodesData.findIndex(node => node.id == curr.id)
- nodesData.splice(removeIndex, 1)
- nodes.splice(removeIndex, 1)
- d3.select(this).remove();
- let relationNode = [],
- relationList = []
- var clickNode = curr.id;//点击节点的名字
- d3.selectAll(".lines").each(function (e) {
- if (e.source.id == curr.id || e.target.id == curr.id) {
- d3.select(this).remove();
- } else {
- relationList.push(e)//出去跟删除节点有关系的其他关系
- }
- //需要删除的节点相关的节点
- if (e.source.id == curr.id) {
- relationNode.push(e.target)
- }
- //需要删除的节点相关的节点
- if (e.target.id == curr.id) {
- relationNode.push(e.source)
- }
- })
- var tempNodeList = JSON.parse(JSON.stringify(relationNode))
- tempNodeList = uniqObjInArray(tempNodeList)
- //区分下级节点是否是孤节点
- tempNodeList.forEach(function (item, index) {
- var hasLine = relationList.findIndex(jtem => jtem.target.id == item.id || jtem.source.id == item.id)
- if (hasLine >= 0) {
- item.notSingle = true
- }
- })
- tempNodeList.forEach(function (item, index) {
- if (!item.notSingle) {
- d3.select('#singleNode' + item.id).remove()
- }
- })
- d3.selectAll(".singleNode").each(function (d, i) {
- var temp = d.id;
- //删除当前需要隐藏的节点
- if (temp == clickNode) {
- removeSingle()
- d3.select(this).remove();
- }
- });
- d3.selectAll(".linksText").each(function (e) {
- if (e.source === curr || e.target === curr) {
- d3.select(this).remove();
- }
- })
- gs.style('opacity', 1);
- links.style('opacity', 1)
- .classed('link-active', false);
- }
- // 关联节点去重重组
- function uniqObjInArray(objarray) {
- let len = objarray.length;
- let tempJson = {
- };
- let res = [];
- for (let i = 0; i < len; i++) {
- //取出每一个对象
- tempJson[JSON.stringify(objarray[i])] = true;
- }
- let keyItems = Object.keys(tempJson);
- for (let j = 0; j < keyItems.length; j++) {
- res.push(JSON.parse(keyItems[j]));
- }
- return res;
- }
- function isLinkLine(node, link) {
- return link.source.id === node.id
- }
- function isLinkNode(currNode, node) {
- if (currNode.id === node.id) {
- return true;
- }
- return linkMap[currNode.id + '-' + node.id] || linkMap[node.id + '-' + currNode.id];
- }
- function largerNode(nodes, currNode, isHover) {
- if (isHover) {
- gs
- .style('stroke-width', 1)
- .filter(node => isNode(currNode, node))
- .style('stroke-width', 10)
- } else {
- gs
- .style('stroke-width', 1)
- }
- }
- function isNode(node, cNode) {
- return true
- }
- // 绘制节点
- gs.append('circle')
- .attr('r', 35)
- .attr('id', function (d) {
- return 'circle' + d.id
- })
- // .attr('fill', function (d, i) {
- // return 'orange'
- // })
- .style("fill", function (node) {
- return "url(#" + "pattern" + node.index + ")";
- })
- .attr('stroke-width', 3)
- // 文字
- var nodeText = gs.append('text')
- // .attr('x', -10)
- // .attr('y', -20)
- // .attr('dy', 10)
-
- .text(function (d) {
- return d.name;
- })
- .attr('text-anchor', 'middle')
- .style('font-size', 11)
- .style("fill", "white")
-
- .attr("dx", function () {
- return (this.getBoundingClientRect().width / 2) * 0.1;
- })
- .attr("dy", function (d) {
- return 35;
- })
- gs.append('title')
- .text((node) => {
- return node.name
- })
- function genLinkMap(relations) {
- const hash = {};
- relations.map(function ({
- source,
- target,
- relation
- }) {
- const key = source + '-' + target;
- if (hash[key]) {
- hash[key] += 1;
- hash[key + '-relation'] += '、' + relation;
- } else {
- hash[key] = 1;
- hash[key + '-relation'] = relation;
- }
- });
- return hash;
- }
- function genLinks(relations) {
- const indexHash = {};
- return relations.map(function ({
- id,
- source,
- target,
- relation,
- value
- }, i) {
- const linkKey = source + '-' + target;
- const count = linkMap[linkKey];
- if (indexHash[linkKey]) {
- console.log(indexHash[linkKey]);
- indexHash[linkKey] -= 1;
- } else {
- indexHash[linkKey] = count - 1;
- }
- return {
- id,
- source: nodesMap[source],
- target: nodesMap[target],
- relation,
- value,
- relations: linkMap[linkKey + '-relation'],
- count: linkMap[linkKey],
- index: indexHash[linkKey]
- }
- })
- }
- // 生成关系连线路径
- function genLinkPath(link) {
- const count = link.count;
- const index = link.index;
- let sx = link.source.x;
- let tx = link.target.x;
- let sy = link.source.y;
- let ty = link.target.y;
- return 'M' + sx + ',' + sy + ' L' + tx + ',' + ty;
- }
- function genNodesMap(nodes) {
- const hash = {};
- nodes.map(function ({
- id,
- name
- }) {
- hash[id] = {
- id,
- name
- };
- });
- return hash;
- }
- // 处理节点文字换行
- function textBreaking(d3text, text) {
- const len = text.length
- if (len <= 3) {
- d3text.append('tspan')
- .attr('x', 0)
- .attr('y', -3)
- .text(text)
- } else {
- const topText = text.substring(0, 3)
- const midText = text.substring(3, 7)
- let botText = text.substring(7, len)
- let topY = -22
- let midY = 8
- let botY = 34
- if (len <= 9) {
- topY += 10
- midY += 10
- } else {
- botText = text.substring(7, 9) + '...'
- }
- d3text.text('')
- d3text.append('tspan')
- .attr('x', 0)
- .attr('y', topY)
- .text(function () {
- return topText
- })
- d3text.append('tspan')
- .attr('x', 0)
- .attr('y', midY)
- .text(function () {
- return midText
- })
- d3text.append('tspan')
- .attr('x', 0)
- .attr('y', botY - 7)
- .text(function () {
- return botText
- })
- }
- }
- // ticked
- function ticked() {
- // 连线路径
- links
- .attr('d', link => genLinkPath(link))
- // 连线文字位置
- linksText
- .attr('x', function (d) { return (d.source.x + d.target.x) / 2 })
- .attr('y', function (d) { return (d.source.y + d.target.y) / 2 })
- // 节点位置
- gs
- .attr('transform', function (d) { return 'translate(' + d.x + ',' + d.y + ')' })
- }
- // drag
- function started(d) {
- if (!d3.event.active) {
- forceSimulation.alphaTarget(0.8).restart() // 设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0, 1]
- }
- d.fx = d.x
- d.fy = d.y
- }
- function dragged(d) {
- d.fx = d3.event.x
- d.fy = d3.event.y
- }
- function ended(d) {
- if (!d3.event.active) {
- forceSimulation.alphaTarget(0)
- }
- d.fx = null
- d.fy = null
- }
- </script>
- </html>
|