当前位置: 首页 > news >正文

60 d3.js 不能正确展示节点连线, 以及一个基础的demo

前言

这个问题是来自于 一位朋友的需求 

主要就是使用 d3.js 来渲染 neo4j 的响应的数据, 做一个可视化的展示 

但是 碰到了一些问题, 比如 需要再连线之间增加 关系的标注 

另外就是 初始化 发过来的代码, 实际上 展示是存在问题的, 这里的主题 就是这几个部分 

d3.js demo 中 节点连线不能正确展示 

demo 代码如下 

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>查询景点并可视化</title><style>#graph {width: 800px;height: 600px;margin: 20px auto;border: 1px solid #ccc;}</style>
</head>
<body>
<h1 style="color:black;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">西双版纳旅游知识</h1>
<input type="text" id="query-input" placeholder="请输入您想查询的知识">
<button onclick="search()">查询</button>
<div id="graph"></div><script src="js/d3.v4.min.js"></script>
<script>function search() {const queryInput = document.getElementById('query-input');const query = queryInput.value;fetch('data/records5.json').then(response => response.json()).then(data => {renderGraph(data, query);}).catch(error => {console.error('Error fetching data:', error);resultContainer.innerHTML = '查询出错,请检查网络连接或数据格式';});}function renderGraph(data, query) {const graphContainer = document.getElementById('graph');graphContainer.innerHTML = '';const svg = d3.select("#graph").append("svg").attr("width", 800).attr("height", 600).style("display", "block").style("margin", "auto");const width = 800; // SVG 容器的宽度const height = 600; // SVG 容器的高度const filteredData = data.filter(item => {const startNodeName = item.p.start.properties.name;const endNodeName = item.p.end.properties.name;return true;});const nodes = {};const links = [];filteredData.forEach(item => {const startNodeName = item.p.start.properties.name;const endNodeName = item.p.end.properties.name;const relationshipType = item.p.segments[0].relationship.type;const relationshipDescription = item.p.segments[0].relationship.properties.description;if (!nodes[startNodeName]) {nodes[startNodeName] = { id: startNodeName, name: startNodeName, description: startNodeName }; // 将节点名称作为描述}if (!nodes[endNodeName]) {nodes[endNodeName] = { id: endNodeName, name: endNodeName, description: endNodeName }; // 将节点名称作为描述}links.push({source: nodes[startNodeName],target: nodes[endNodeName],type: relationshipType,description: relationshipDescription});});const simulation = d3.forceSimulation(Object.values(nodes)).force("link", d3.forceLink(links).id(d => d.id)).force("charge", d3.forceManyBody().strength(-200)) // 添加电荷力,使节点相互排斥.force("center", d3.forceCenter(width / 2, height / 2)) // 添加中心引力,吸引所有节点到中心.force("collision", d3.forceCollide(18)); // 添加碰撞检测力,确保节点之间保持一定的距离// 绘制节点const node = svg.selectAll("circle").data(Object.values(nodes)).enter().append("circle").attr("r", 10).attr("cx", width/2) // 设置x坐标为中心.attr("cy", height/2) // 设置y坐标为中心.attr("fill", "#964c5d").append("title") // 添加 title 元素以显示节点描述.text(d => d.description);const link = svg.selectAll("line").data(links).enter().append("line").attr("stroke", "#964c5d").attr("stroke-opacity", 0.6).attr("stroke-width", 1).append("title") // 添加 title 元素以显示关系描述.text(d => d.description);simulation.on("tick", () => {link.attr("x1", d => d.source.x).attr("y1", d => d.source.y).attr("x2", d => d.target.x).attr("y2", d => d.target.y);node.attr("cx", d => d.x).attr("cy", d => d.y);});}
</script>
</body>
</html>

然后 record.json 的测试数据如下, 可以直接使用 

[{"p": {"start": {"identity": 7823,"labels": ["景点名称"],"properties": {"name": "曼听公园"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823"},"end": {"identity": 7972,"labels": ["景点属性值"],"properties": {"name": "4A"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7972"},"segments": [{"start": {"identity": 7823,"labels": ["景点名称"],"properties": {"name": "曼听公园"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823"},"relationship": {"identity": 0,"start": 7823,"end": 7972,"type": "景点关系","properties": {"景点关系": "星级"},"elementId": "5:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:0","startNodeElementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823","endNodeElementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7972"},"end": {"identity": 7972,"labels": ["景点属性值"],"properties": {"name": "4A"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7972"}}],"length": 1.0}},{"p": {"start": {"identity": 7823,"labels": ["景点名称"],"properties": {"name": "曼听公园"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823"},"end": {"identity": 7973,"labels": ["景点属性值"],"properties": {"name": "4.5"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7973"},"segments": [{"start": {"identity": 7823,"labels": ["景点名称"],"properties": {"name": "曼听公园"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823"},"relationship": {"identity": 1,"start": 7823,"end": 7973,"type": "景点关系","properties": {"景点关系": "评分"},"elementId": "5:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:1","startNodeElementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823","endNodeElementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7973"},"end": {"identity": 7973,"labels": ["景点属性值"],"properties": {"name": "4.5"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7973"}}],"length": 1.0}},{"p": {"start": {"identity": 7823,"labels": ["景点名称"],"properties": {"name": "曼听公园"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823"},"end": {"identity": 7974,"labels": ["景点属性值"],"properties": {"name": "云南省西双版纳傣族自治州景洪市曼听路35号"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7974"},"segments": [{"start": {"identity": 7823,"labels": ["景点名称"],"properties": {"name": "曼听公园"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823"},"relationship": {"identity": 2,"start": 7823,"end": 7974,"type": "景点关系","properties": {"景点关系": "地址"},"elementId": "5:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:2","startNodeElementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7823","endNodeElementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7974"},"end": {"identity": 7974,"labels": ["景点属性值"],"properties": {"name": "云南省西双版纳傣族自治州景洪市曼听路35号"},"elementId": "4:fea7ca27-49b0-48cd-b5eb-17bbeb53c807:7974"}}],"length": 1.0}}
]

然后 页面展示如下, 可以看到 页面绘制是存在问题的, 四个点的坐标 一样 

然后 也就没有了 点和点之间的连线, 然后 出现的现象就是这样  

d3.js demo 中 节点连线展示问题解决 

注释掉这里两个 append("title"), 然后 就可以正常展示了 

展示结果如下

一个完整的 d3.js demo 

这个demo包含了基础的 点线的展示 

节点的拖动, 关联线的标注信息 等等 

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>查询景点并可视化</title><style>#graph {width: 800px;height: 600px;margin: 20px auto;border: 1px solid #ccc;}.link-annotation {font-size: 10px;dominant-baseline: middle;text-anchor: middle;}</style>
</head>
<body>
<h1 style="color:black;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">西双版纳旅游知识</h1>
<input type="text" id="query-input" placeholder="请输入您想查询的知识">
<button onclick="search()">查询</button>
<div id="graph"></div><script src="js/d3.v4.min.js"></script>
<script>let simulation = null;function search() {const queryInput = document.getElementById('query-input');const query = queryInput.value;fetch('data/records7.json').then(response => response.json()).then(data => {renderGraph(data, query);}).catch(error => {console.error('Error fetching data:', error);resultContainer.innerHTML = '查询出错,请检查网络连接或数据格式';});}function renderGraph(data, query) {const graphContainer = document.getElementById('graph');graphContainer.innerHTML = '';const svg = d3.select(graphContainer).append("svg").attr("width", 800).attr("height", 600).style("display", "block").style("margin", "auto");const width = 800; // SVG 容器的宽度const height = 600; // SVG 容器的高度const filteredData = data.filter(item => {const startNodeName = item.p.start.properties.name;const endNodeName = item.p.end.properties.name;return true;});const nodes = {};const links = [];filteredData.forEach(item => {const startNodeName = item.p.start.properties.name;const endNodeName = item.p.end.properties.name;const relationshipType = item.p.segments[0].relationship.type;const relationshipDescription = item.p.segments[0].relationship.properties[item.p.segments[0].relationship.type];if (!nodes[startNodeName]) {nodes[startNodeName] = { id: startNodeName, name: startNodeName, description: startNodeName, type: 'start' };}if (!nodes[endNodeName]) {nodes[endNodeName] = { id: endNodeName, name: endNodeName, description: endNodeName, type: 'end' };}links.push({source: nodes[startNodeName],target: nodes[endNodeName],type: relationshipType,description: relationshipDescription});});simulation = d3.forceSimulation(Object.values(nodes)).force("link", d3.forceLink(links).id(d => d.id)).force("charge", d3.forceManyBody().strength(-400)).force("center", d3.forceCenter(width / 2, height / 2)).force("collision", d3.forceCollide(50));const node = svg.selectAll("circle").data(Object.values(nodes)).enter().append("circle").attr("r", 20).attr("fill", d => d.type === 'start' ? "blue" : "pink") // 根据节点类型设置颜色.text(d => d.description).attr("id", d => d.id).call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));const nodeLabel = svg.selectAll("text").data(Object.values(nodes)).enter().append("text").attr("x", d => d.x).attr("y", d => d.y).attr("dy", "0.35em").attr("text-anchor", "middle").style("font-size", "12px").style("fill", "green").text(d => d.name);const link = svg.selectAll(".link").data(links).enter().append("line").attr("class", "link").attr("stroke", "blue").attr("stroke-opacity", 0.6).attr("stroke-width", 1);let annoEleList = null;function ticked() {link.attr("x1", d => d.source.x).attr("y1", d => d.source.y).attr("x2", d => d.target.x).attr("y2", d => d.target.y);node.attr("cx", d => d.x).attr("cy", d => d.y);nodeLabel.attr("x", d => d.x).attr("y", d => d.y);// 在 link 上面添加标注if(annoEleList) {for(let i in annoEleList) {annoEleList[i].remove()}}annoEleList = []links.forEach(function(linkData) {let annoEle = svg.append("text").attr("class", "link-annotation").text(linkData.description)annoEleList.push(annoEle)let midX = linkData.source.x + ((linkData.target.x - linkData.source.x) / 2)let midY = linkData.source.y + ((linkData.target.y - linkData.source.y) / 2)annoEle.attr("x", midX).attr("y", midY);});}simulation.on("tick", ticked);}// 拖动函数代码var dragging = false;// 开始拖动并更新相应的点function dragstarted(d) {if (!d3.event.active) simulation.alphaTarget(0.3).restart();d.fx = d.x;d.fy = d.y;dragging = true;}// 拖动进行中function dragged(d) {d.fx = d3.event.x;d.fy = d3.event.y;}// 拖动结束function dragended(d) {if (!d3.event.active) simulation.alphaTarget(0);d.fx = null;d.fy = null;dragging = false;}</script>
</body>
</html>

展示效果如下 

完 

http://www.dtcms.com/a/554021.html

相关文章:

  • 优质网站建设是哪家北京论坛网站建设
  • 黑马商城day8-ES01
  • 创意网站建设公司阿里云宝塔面板一键安装wordpress
  • F043 vue+flask天气预测可视化系统大数据(浅色版)+机器学习+管理端+爬虫+超酷界面+顶级可视化水平
  • 电脑无法识别WiFi 7路由器的解决方式
  • 海南网站推广微商目前十大火爆产品
  • Nestjs框架: 微服务架构实践与技术选型指南
  • 网络请求完整指南 - 从原理到实践
  • PHP网站建设项目经验动态wordpress动态主题
  • 企业网站建设项目描述saas小程序开发
  • 各大门户网站怎么做推广如何做网站 优帮云
  • Python在云计算中的应用:AWS Lambda函数实战
  • Spring Cloud OpenFeign进行服务间调用时,传递自定义的请求头信息及报错解决
  • 基于单片机的 AI 智能快递柜设计与实现
  • 【医学影像 AI】视网膜图像多类别分割中的“段内误分类”
  • python3.13安装教程(附安装包),【2025】python3.13安装图文教程
  • 深圳专业建站多少钱线上渠道推广
  • ROS开发中`laser_scan_matcher`节点启动失败问题的分析与解决
  • 南昌装修网站建设菠菜网站的代理怎么做
  • c++调用客户端库与kafka交互
  • 用Liunx service方式管理SuperMap iServer
  • 邢台网站设计哪家专业wordpress主题添加后台设置选项
  • 京东网站建设的策划书如何做120急救网站
  • 佛山新网站建设服务安卓开发是做什么的
  • 普通园区摄像头如何将视频传输至云端平台
  • HDMI接口与串口:核心知识点解析及全方位区别对比
  • sql server 备份恢复全备和日志备份时间相近恢复失败
  • 回溯剪枝trick
  • 网页设计与制作用什么软件做北京公司排名seo
  • 什么伪原创网站好深圳全面放开